mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
48dcd37740 | ||
![]() |
8e619756b9 | ||
![]() |
6c6b47db7d | ||
![]() |
a126844272 | ||
![]() |
9b3086ff2e | ||
![]() |
89d1842d59 | ||
![]() |
d44067e952 | ||
![]() |
0370eaa619 | ||
![]() |
35c393495a | ||
![]() |
f19618e017 | ||
![]() |
3a68802ae7 | ||
![]() |
344eeb7a3b | ||
![]() |
4ad58684b4 | ||
![]() |
30e35a895d | ||
![]() |
37414be4bf | ||
![]() |
a37ca4c0e5 | ||
![]() |
408b1a68d0 | ||
![]() |
40da44222e | ||
![]() |
b0efdca928 | ||
![]() |
d61198b47f | ||
![]() |
62433f6e10 | ||
![]() |
c9ae39508f | ||
![]() |
44836a24bb | ||
![]() |
80e60bd0c8 | ||
![]() |
9f38b957e1 | ||
![]() |
032aa24ab5 | ||
![]() |
afc45e1cbc | ||
![]() |
bd598deddd | ||
![]() |
952fb3a7f1 | ||
![]() |
fb62074d20 | ||
![]() |
56132e26ed | ||
![]() |
3239bdeb33 | ||
![]() |
142885821e | ||
![]() |
34941c599a | ||
![]() |
58c8f1896c | ||
![]() |
6611539491 | ||
![]() |
69729e5257 | ||
![]() |
535158c5e9 | ||
![]() |
61e2d880c2 | ||
![]() |
72889fcdde | ||
![]() |
905ee8b8a9 | ||
![]() |
81a9aa6938 | ||
![]() |
046238a29f | ||
![]() |
9751825ed7 | ||
![]() |
c19ee65dd1 | ||
![]() |
282f00959d | ||
![]() |
104a6063a3 | ||
![]() |
95e47e3407 | ||
![]() |
cb5795e64b | ||
![]() |
746da913f0 | ||
![]() |
2682399600 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.gradle
|
||||
build
|
2
.idea/.gitignore
generated
vendored
Normal file
2
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
workspace.xml
|
||||
markdown-navigator*
|
3068
.idea/workspace.xml
generated
3068
.idea/workspace.xml
generated
File diff suppressed because it is too large
Load Diff
53
README.md
53
README.md
@@ -3,49 +3,46 @@ Another Android Monero Wallet
|
||||
|
||||
### QUICKSTART
|
||||
- Download APK (Release) and install it
|
||||
- Copy over synced wallet (all three files) onto sdcard in directory Monerujo (created first time app is started)
|
||||
- Start app (again)
|
||||
- Run the App and select "Generate Wallet" to create a new wallet or recover a wallet
|
||||
- Advanced users could copy over synced wallet files (all files) onto sdcard in directory Monerujo (created first time App is started)
|
||||
- see the [FAQ](doc/FAQ.md)
|
||||
|
||||
### Disclaimer
|
||||
This is my first serious Android App.
|
||||
You may loose all your Moneroj if you use this App.
|
||||
|
||||
### Notes
|
||||
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
|
||||
- Special thanks to /u/gkruja for inspiration to pick this project up again
|
||||
- Based off monero v0.10.3.1 with pull requests #2238 & #2239 applied => so can be used in mainnet!
|
||||
### Random Notes
|
||||
- Based off monero v0.10.3.1 with pull requests #2238, #2239 and #2289 applied => so can be used in mainnet!
|
||||
- currently only android32
|
||||
- sorry for my messups in github
|
||||
- this is more of a proof of concept
|
||||
- currently only use is checking incoming/outgoing transactions
|
||||
- works in testnet & mainnet (please use your own daemons)
|
||||
- takes forever to sync on mainnet (even with own daemon)
|
||||
- ~~currently only use is checking incoming/outgoing transactions~~
|
||||
- works in testnet & mainnet
|
||||
- takes forever to sync due to 32-bit architecture
|
||||
- use your own daemon - it's easy
|
||||
- screen stays on until first sync is complete
|
||||
- saves wallet only on first sync and when sending transactions or editing notes
|
||||
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
|
||||
|
||||
### TODO
|
||||
- don't have the screen on for first sync - use IntentService with WakeLock instead?
|
||||
- make it pretty
|
||||
- show current block height - is that relevant?
|
||||
- License Dialog
|
||||
- support for right-to-left layouts
|
||||
- visibility of methods/classes
|
||||
- adjust layout so we can use bigger font sizes
|
||||
- sensible error dialogs (e.g. when no write permissions granted) instead of just crashing on purpose
|
||||
- sensible loading/saving progress bars instead of just freezing up
|
||||
- spend monero - not so difficult with wallet api
|
||||
- figure out how to make it all flow better (loading/saving takes forever and does not run in background)
|
||||
- currently loading in background thread produces segfaults in JNI
|
||||
- check licenses of included libraries
|
||||
- wallet backup functions
|
||||
- adjust layout so we can use bigger font sizes (maybe show only 5 decimal places instead of 12 in main view)
|
||||
- review visibility of methods/classes
|
||||
- more sensible error dialogs ~~(e.g. when no write permissions granted) instead of just crashing on purpose~~
|
||||
- check licenses of included libraries; License Dialog
|
||||
- ~~make it pretty~~ (decided to go with "form follows function")
|
||||
- ~~spend monero - not so difficult with wallet api~~
|
||||
|
||||
### Issues
|
||||
- occasional crashes ...
|
||||
- Pending incoming transactions disappear after reload
|
||||
|
||||
### HOW TO BUILD
|
||||
No need to build. Binaries are included:
|
||||
|
||||
- openssl-1.0.2l
|
||||
- monero-v0.10.3.1 + pull requests #2238 & #2239
|
||||
- monero-v0.10.3.1 + pull requests #2238, #2239 and #2289
|
||||
- boost_1_64_0
|
||||
|
||||
If you want to build - fire up Android Studio and build. Also you can rebuild all of the above.
|
||||
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
|
||||
|
||||
Then, fire up Android Studio and build the APK.
|
||||
|
||||
### Donations
|
||||
4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk
|
||||
|
3
app/.gitignore
vendored
Normal file
3
app/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.externalNativeBuild
|
||||
build
|
||||
app.iml
|
@@ -11,13 +11,13 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs)
|
||||
# OpenSSL
|
||||
############
|
||||
|
||||
add_library(crypto SHARED IMPORTED)
|
||||
add_library(crypto STATIC IMPORTED)
|
||||
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.so)
|
||||
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
|
||||
|
||||
add_library(ssl SHARED IMPORTED)
|
||||
add_library(ssl STATIC IMPORTED)
|
||||
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.so)
|
||||
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
|
||||
|
||||
############
|
||||
# Boost
|
||||
@@ -63,55 +63,55 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
|
||||
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct)
|
||||
#############
|
||||
|
||||
add_library(wallet SHARED IMPORTED)
|
||||
add_library(wallet STATIC IMPORTED)
|
||||
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
|
||||
|
||||
add_library(cryptonote_core SHARED IMPORTED)
|
||||
add_library(cryptonote_core STATIC IMPORTED)
|
||||
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a)
|
||||
|
||||
add_library(cryptonote_basic SHARED IMPORTED)
|
||||
add_library(cryptonote_basic STATIC IMPORTED)
|
||||
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a)
|
||||
|
||||
add_library(mnemonics SHARED IMPORTED)
|
||||
add_library(mnemonics STATIC IMPORTED)
|
||||
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
|
||||
|
||||
add_library(common SHARED IMPORTED)
|
||||
add_library(common STATIC IMPORTED)
|
||||
set_target_properties(common PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
|
||||
|
||||
add_library(cncrypto SHARED IMPORTED)
|
||||
add_library(cncrypto STATIC IMPORTED)
|
||||
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
|
||||
|
||||
add_library(ringct SHARED IMPORTED)
|
||||
add_library(ringct STATIC IMPORTED)
|
||||
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
|
||||
|
||||
#####
|
||||
|
||||
add_library(p2p SHARED IMPORTED)
|
||||
add_library(p2p STATIC IMPORTED)
|
||||
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
|
||||
|
||||
add_library(blockchain_db SHARED IMPORTED)
|
||||
add_library(blockchain_db STATIC IMPORTED)
|
||||
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a)
|
||||
|
||||
add_library(lmdb SHARED IMPORTED)
|
||||
add_library(lmdb STATIC IMPORTED)
|
||||
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
|
||||
|
||||
add_library(easylogging SHARED IMPORTED)
|
||||
add_library(easylogging STATIC IMPORTED)
|
||||
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
|
||||
|
||||
add_library(unbound SHARED IMPORTED)
|
||||
add_library(unbound STATIC IMPORTED)
|
||||
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.so)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
|
||||
|
||||
####
|
||||
add_library(epee STATIC IMPORTED)
|
||||
@@ -141,9 +141,10 @@ target_link_libraries( monerujo
|
||||
cryptonote_core
|
||||
cryptonote_basic
|
||||
mnemonics
|
||||
cncrypto
|
||||
ringct
|
||||
common
|
||||
cncrypto
|
||||
|
||||
blockchain_db
|
||||
lmdb
|
||||
#easylogging # not for 0.10.3.1
|
||||
|
139
app/app.iml
139
app/app.iml
@@ -1,139 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="native-android-gradle" name="Native-Android-Gradle">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</task>
|
||||
</afterSyncTasks>
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/cpp" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/cmake" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="runner-0.5" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="espresso-idling-resource-2.2.2" level="project" />
|
||||
<orderEntry type="library" exported="" name="constraint-layout-1.0.2" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-library-1.3" level="project" />
|
||||
<orderEntry type="library" exported="" name="transition-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-integration-1.3" level="project" />
|
||||
<orderEntry type="library" exported="" name="design-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-ui-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="cardview-v7-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="jsr305-2.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-utils-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-fragment-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="espresso-core-2.2.2" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="exposed-instrumentation-api-publish-0.5" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="rules-0.5" level="project" />
|
||||
<orderEntry type="library" exported="" name="constraint-layout-solver-1.0.2" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="javax.annotation-api-1.2" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="javax.inject-1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="javawriter-2.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-media-compat-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-vector-drawable-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-compat-25.3.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="animated-vector-drawable-25.3.1" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "0.1"
|
||||
versionCode 5
|
||||
versionName "0.4.1"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
@@ -17,17 +17,28 @@
|
||||
|
||||
<activity
|
||||
android:name=".WalletActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/wallet_activity_name"
|
||||
android:launchMode="singleTop"></activity>
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:label="@string/app_name">
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.WalletService"
|
||||
android:description="@string/service_description"
|
||||
android:exported="false"
|
||||
android:label="Monero Wallet Service" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,18 @@
|
||||
//
|
||||
// Created by m2049r on 15.04.2017.
|
||||
//
|
||||
/**
|
||||
* Copyright (c) 2017 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_WALLET_LIB_H
|
||||
#define XMRWALLET_WALLET_LIB_H
|
||||
|
370
app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
Normal file
370
app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class GenerateReviewFragment extends Fragment {
|
||||
static final String TAG = "GenerateReviewFragment";
|
||||
static final public String VIEW_DETAILS = "details";
|
||||
static final public String VIEW_ACCEPT = "accept";
|
||||
static final public String VIEW_WALLET = "wallet";
|
||||
|
||||
ProgressBar pbProgress;
|
||||
TextView tvWalletName;
|
||||
TextView tvWalletPassword;
|
||||
TextView tvWalletAddress;
|
||||
TextView tvWalletMnemonic;
|
||||
TextView tvWalletViewKey;
|
||||
TextView tvWalletSpendKey;
|
||||
Button bAccept;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.gen_review_fragment, container, false);
|
||||
|
||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||
tvWalletName = (TextView) view.findViewById(R.id.tvWalletName);
|
||||
tvWalletPassword = (TextView) view.findViewById(R.id.tvWalletPassword);
|
||||
tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress);
|
||||
tvWalletViewKey = (TextView) view.findViewById(R.id.tvWalletViewKey);
|
||||
tvWalletSpendKey = (TextView) view.findViewById(R.id.tvWalletSpendKey);
|
||||
tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic);
|
||||
|
||||
bAccept = (Button) view.findViewById(R.id.bAccept);
|
||||
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
tvWalletMnemonic.setTextIsSelectable(testnet);
|
||||
|
||||
bAccept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
acceptWallet();
|
||||
}
|
||||
});
|
||||
|
||||
showProgress();
|
||||
|
||||
Bundle b = getArguments();
|
||||
String type = b.getString("type");
|
||||
if (!type.equals(VIEW_WALLET)) {
|
||||
String name = b.getString("name");
|
||||
String password = b.getString("password");
|
||||
tvWalletName.setText(new File(name).getName());
|
||||
show(name, password, type);
|
||||
} else {
|
||||
show(walletCallback.getWallet(), null, type);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
private void acceptWallet() {
|
||||
String name = tvWalletName.getText().toString();
|
||||
String password = tvWalletPassword.getText().toString();
|
||||
bAccept.setEnabled(false);
|
||||
acceptCallback.onAccept(name, password);
|
||||
}
|
||||
|
||||
private void show(final String walletPath, final String password, final String type) {
|
||||
new Thread(null,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
show(wallet, password, type);
|
||||
wallet.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
, "DetailsReview", MoneroHandlerThread.THREAD_STACK_SIZE).start();
|
||||
}
|
||||
|
||||
private void show(final Wallet wallet, final String password, final String type) {
|
||||
if (type.equals(GenerateReviewFragment.VIEW_ACCEPT)) {
|
||||
tvWalletPassword.setText(password);
|
||||
bAccept.setVisibility(View.VISIBLE);
|
||||
bAccept.setEnabled(true);
|
||||
}
|
||||
tvWalletName.setText(wallet.getName());
|
||||
tvWalletAddress.setText(wallet.getAddress());
|
||||
tvWalletMnemonic.setText(wallet.getSeed());
|
||||
tvWalletViewKey.setText(wallet.getSecretViewKey());
|
||||
String spend = wallet.isWatchOnly() ? "" : "not available - use seed for recovery";
|
||||
if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet
|
||||
tvWalletSpendKey.setText(spend);
|
||||
} else {
|
||||
tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly));
|
||||
}
|
||||
hideProgress();
|
||||
}
|
||||
|
||||
GenerateReviewFragment.Listener acceptCallback = null;
|
||||
GenerateReviewFragment.ListenerWithWallet walletCallback = null;
|
||||
|
||||
public interface Listener {
|
||||
void onAccept(String name, String password);
|
||||
}
|
||||
|
||||
public interface ListenerWithWallet {
|
||||
Wallet getWallet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof GenerateReviewFragment.Listener) {
|
||||
this.acceptCallback = (GenerateReviewFragment.Listener) context;
|
||||
} else if (context instanceof GenerateReviewFragment.ListenerWithWallet) {
|
||||
this.walletCallback = (GenerateReviewFragment.ListenerWithWallet) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
public void showProgress() {
|
||||
pbProgress.setIndeterminate(true);
|
||||
pbProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideProgress() {
|
||||
pbProgress.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
369
app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java
Normal file
369
app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
379
app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
Normal file
379
app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
248
app/src/main/java/com/m2049r/xmrwallet/TxFragment.java
Normal file
248
app/src/main/java/com/m2049r/xmrwallet/TxFragment.java
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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 android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Transfer;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class TxFragment extends Fragment {
|
||||
static final String TAG = "TxFragment";
|
||||
|
||||
static public final String ARG_INFO = "info";
|
||||
|
||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public TxFragment() {
|
||||
super();
|
||||
Calendar cal = Calendar.getInstance();
|
||||
TimeZone tz = cal.getTimeZone(); //get the local time zone.
|
||||
TS_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
TextView tvTxTimestamp;
|
||||
TextView tvTxId;
|
||||
TextView tvTxKey;
|
||||
TextView tvTxPaymentId;
|
||||
TextView tvTxBlockheight;
|
||||
TextView tvTxAmount;
|
||||
TextView tvTxFee;
|
||||
TextView tvTxTransfers;
|
||||
TextView etTxNotes;
|
||||
Button bCopy;
|
||||
Button bTxNotes;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.tx_fragment, container, false);
|
||||
|
||||
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
|
||||
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
||||
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
|
||||
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
|
||||
tvTxBlockheight = (TextView) view.findViewById(R.id.tvTxBlockheight);
|
||||
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
|
||||
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
|
||||
tvTxTransfers = (TextView) view.findViewById(R.id.tvTxTransfers);
|
||||
etTxNotes = (TextView) view.findViewById(R.id.etTxNotes);
|
||||
bCopy = (Button) view.findViewById(R.id.bCopy);
|
||||
bTxNotes = (Button) view.findViewById(R.id.bTxNotes);
|
||||
|
||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
bCopy.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
copyToClipboard();
|
||||
}
|
||||
});
|
||||
|
||||
bTxNotes.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
info.notes = null; // force reload on next view
|
||||
bTxNotes.setEnabled(false);
|
||||
etTxNotes.setEnabled(false);
|
||||
activityCallback.onSetNote(info.hash, etTxNotes.getText().toString());
|
||||
}
|
||||
});
|
||||
|
||||
Bundle args = getArguments();
|
||||
TransactionInfo info = args.getParcelable(ARG_INFO);
|
||||
show(info);
|
||||
return view;
|
||||
}
|
||||
|
||||
public void onNotesSet(boolean reload) {
|
||||
bTxNotes.setEnabled(true);
|
||||
etTxNotes.setEnabled(true);
|
||||
if (reload) {
|
||||
loadNotes(this.info);
|
||||
}
|
||||
}
|
||||
|
||||
void copyToClipboard() {
|
||||
if (this.info == null) return;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getString(R.string.tx_address)).append(": ");
|
||||
sb.append(activityCallback.getWalletAddress()).append("\n");
|
||||
sb.append(getString(R.string.tx_id)).append(": ");
|
||||
sb.append(info.hash).append("\n");
|
||||
sb.append(getString(R.string.tx_key)).append(": ");
|
||||
sb.append(info.txKey.isEmpty() ? "-" : info.txKey).append("\n");
|
||||
sb.append(getString(R.string.tx_paymentId)).append(": ");
|
||||
sb.append(info.paymentId).append("\n");
|
||||
sb.append(getString(R.string.tx_amount)).append(": ");
|
||||
sb.append((info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-"));
|
||||
sb.append(Wallet.getDisplayAmount(info.amount)).append("\n");
|
||||
sb.append(getString(R.string.tx_fee)).append(": ");
|
||||
sb.append(Wallet.getDisplayAmount(info.fee)).append("\n");
|
||||
sb.append(getString(R.string.tx_notes)).append(": ");
|
||||
String oneLineNotes = info.notes.replace("\n", " ; ");
|
||||
sb.append(oneLineNotes.isEmpty() ? "-" : oneLineNotes).append("\n");
|
||||
sb.append(getString(R.string.tx_timestamp)).append(": ");
|
||||
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n");
|
||||
sb.append(getString(R.string.tx_blockheight)).append(": ");
|
||||
if (info.isPending) {
|
||||
sb.append(getString(R.string.tx_pending)).append("\n");
|
||||
} else if (info.isFailed) {
|
||||
sb.append(getString(R.string.tx_failed)).append("\n");
|
||||
} else {
|
||||
sb.append(info.blockheight).append("\n");
|
||||
}
|
||||
sb.append(getString(R.string.tx_transfers)).append(": ");
|
||||
if (info.transfers != null) {
|
||||
boolean comma = false;
|
||||
for (Transfer transfer : info.transfers) {
|
||||
if (comma) {
|
||||
sb.append(",");
|
||||
} else {
|
||||
comma = true;
|
||||
}
|
||||
sb.append("[").append(transfer.address.substring(0, 6)).append("] ");
|
||||
sb.append(Wallet.getDisplayAmount(transfer.amount));
|
||||
}
|
||||
} else {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append("\n");
|
||||
ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText(getString(R.string.tx_copy_label), sb.toString());
|
||||
clipboardManager.setPrimaryClip(clip);
|
||||
Toast.makeText(getActivity(), getString(R.string.tx_copy_message), Toast.LENGTH_SHORT).show();
|
||||
//Log.d(TAG, sb.toString());
|
||||
}
|
||||
|
||||
TransactionInfo info = null;
|
||||
|
||||
void loadNotes(TransactionInfo info) {
|
||||
if (info.notes == null) {
|
||||
info.notes = activityCallback.getTxNotes(info.hash);
|
||||
//Log.d(TAG, "NOTES:" + info.notes + ":");
|
||||
}
|
||||
etTxNotes.setText(info.notes);
|
||||
}
|
||||
|
||||
private void show(TransactionInfo info) {
|
||||
if (info.txKey == null) {
|
||||
info.txKey = activityCallback.getTxKey(info.hash);
|
||||
//Log.d(TAG, "TXKEY:" + info.txKey + ":");
|
||||
}
|
||||
loadNotes(info);
|
||||
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
|
||||
tvTxId.setText(info.hash);
|
||||
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
|
||||
tvTxPaymentId.setText(info.paymentId);
|
||||
if (info.isPending) {
|
||||
tvTxBlockheight.setText(getString(R.string.tx_pending));
|
||||
} else if (info.isFailed) {
|
||||
tvTxBlockheight.setText(getString(R.string.tx_failed));
|
||||
} else {
|
||||
tvTxBlockheight.setText("" + info.blockheight);
|
||||
}
|
||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
|
||||
tvTxFee.setText(Wallet.getDisplayAmount(info.fee));
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (info.transfers != null) {
|
||||
boolean newline = false;
|
||||
for (Transfer transfer : info.transfers) {
|
||||
if (newline) {
|
||||
sb.append("\n");
|
||||
} else {
|
||||
newline = true;
|
||||
}
|
||||
sb.append("[").append(transfer.address.substring(0, 6)).append("] ");
|
||||
sb.append(Wallet.getDisplayAmount(transfer.amount));
|
||||
}
|
||||
} else {
|
||||
sb.append("-");
|
||||
}
|
||||
tvTxTransfers.setText(sb.toString());
|
||||
this.info = info;
|
||||
bCopy.setEnabled(true);
|
||||
}
|
||||
|
||||
TxFragment.Listener activityCallback;
|
||||
|
||||
public interface Listener {
|
||||
String getWalletAddress();
|
||||
|
||||
String getTxKey(String hash);
|
||||
|
||||
String getTxNotes(String hash);
|
||||
|
||||
void onSetNote(String txId, String notes);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof TxFragment.Listener) {
|
||||
this.activityCallback = (TxFragment.Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
240
app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java
Normal file
240
app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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 android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Transfer;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class WalletFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener {
|
||||
private static final String TAG = "WalletFragment";
|
||||
private TransactionInfoAdapter adapter;
|
||||
private NumberFormat formatter = NumberFormat.getInstance();
|
||||
|
||||
TextView tvBalance;
|
||||
TextView tvUnlockedBalance;
|
||||
TextView tvBlockHeightProgress;
|
||||
TextView tvConnectionStatus;
|
||||
LinearLayout llProgress;
|
||||
TextView tvProgress;
|
||||
ProgressBar pbProgress;
|
||||
Button bSend;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.wallet_fragment, container, false);
|
||||
|
||||
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
|
||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||
llProgress = (LinearLayout) view.findViewById(R.id.llProgress);
|
||||
tvBalance = (TextView) view.findViewById(R.id.tvBalance);
|
||||
tvUnlockedBalance = (TextView) view.findViewById(R.id.tvUnlockedBalance);
|
||||
tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress);
|
||||
tvConnectionStatus = (TextView) view.findViewById(R.id.tvConnectionStatus);
|
||||
|
||||
bSend = (Button) view.findViewById(R.id.bSend);
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
||||
RecyclerView.ItemDecoration itemDecoration = new
|
||||
DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
|
||||
recyclerView.addItemDecoration(itemDecoration);
|
||||
|
||||
this.adapter = new TransactionInfoAdapter(this);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
bSend.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activityCallback.onSendRequest();
|
||||
}
|
||||
});
|
||||
|
||||
if (activityCallback.isSynced()) {
|
||||
onSynced();
|
||||
}
|
||||
|
||||
// activityCallback.setTitle(getString(R.string.status_wallet_loading));
|
||||
|
||||
activityCallback.forceUpdate();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
// Callbacks from TransactionInfoAdapter
|
||||
@Override
|
||||
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||
activityCallback.onTxDetailsRequest(infoItem);
|
||||
}
|
||||
|
||||
// called from activity
|
||||
public void onRefreshed(final Wallet wallet, final boolean full) {
|
||||
Log.d(TAG, "onRefreshed()");
|
||||
if (full) {
|
||||
List<TransactionInfo> list = wallet.getHistory().getAll();
|
||||
adapter.setInfos(list);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
updateStatus(wallet);
|
||||
}
|
||||
|
||||
public void onSynced() {
|
||||
if (!activityCallback.isWatchOnly() && WalletManager.getInstance().isTestNet()) {
|
||||
bSend.setVisibility(View.VISIBLE);
|
||||
bSend.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void onProgress(final String text) {
|
||||
if (text != null) {
|
||||
tvProgress.setText(text);
|
||||
showProgress();
|
||||
} else {
|
||||
hideProgress();
|
||||
tvProgress.setText(getString(R.string.status_working));
|
||||
onProgress(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public void onProgress(final int n) {
|
||||
if (n >= 0) {
|
||||
pbProgress.setIndeterminate(false);
|
||||
pbProgress.setProgress(n);
|
||||
} else {
|
||||
pbProgress.setIndeterminate(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void showProgress() {
|
||||
llProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideProgress() {
|
||||
llProgress.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
String setActivityTitle(Wallet wallet) {
|
||||
if (wallet == null) return null;
|
||||
String shortName = wallet.getName();
|
||||
if (shortName.length() > 16) {
|
||||
shortName = shortName.substring(0, 14) + "...";
|
||||
}
|
||||
String title = "[" + wallet.getAddress().substring(0, 6) + "] "
|
||||
+ shortName
|
||||
+ (wallet.isWatchOnly() ? " " + getString(R.string.watchonly_label) : "");
|
||||
activityCallback.setTitle(title);
|
||||
Log.d(TAG, "wallet title is " + title);
|
||||
return title;
|
||||
}
|
||||
|
||||
private long firstBlock = 0;
|
||||
private String walletTitle = null;
|
||||
|
||||
private void updateStatus(Wallet wallet) {
|
||||
Log.d(TAG, "updateStatus()");
|
||||
if (walletTitle == null) {
|
||||
walletTitle = setActivityTitle(wallet);
|
||||
onProgress(100); // of loading
|
||||
}
|
||||
tvBalance.setText(Wallet.getDisplayAmount(wallet.getBalance()));
|
||||
tvUnlockedBalance.setText(Wallet.getDisplayAmount(wallet.getUnlockedBalance()));
|
||||
String sync = "";
|
||||
if (!activityCallback.hasBoundService())
|
||||
throw new IllegalStateException("WalletService not bound.");
|
||||
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
||||
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||
long daemonHeight = activityCallback.getDaemonHeight();
|
||||
if (!wallet.isSynchronized()) {
|
||||
long n = daemonHeight - wallet.getBlockChainHeight();
|
||||
sync = formatter.format(n) + " " + getString(R.string.status_remaining);
|
||||
if (firstBlock == 0) {
|
||||
firstBlock = wallet.getBlockChainHeight();
|
||||
}
|
||||
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
|
||||
onProgress(getString(R.string.status_syncing) + " " + sync);
|
||||
if (x == 0) x = -1;
|
||||
onProgress(x);
|
||||
} else {
|
||||
sync = getString(R.string.status_synced) + ": " + formatter.format(wallet.getBlockChainHeight());
|
||||
}
|
||||
}
|
||||
String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));
|
||||
tvBlockHeightProgress.setText(sync);
|
||||
tvConnectionStatus.setText(net + " " + daemonConnected.toString().substring(17));
|
||||
}
|
||||
|
||||
Listener activityCallback;
|
||||
|
||||
// Container Activity must implement this interface
|
||||
public interface Listener {
|
||||
boolean hasBoundService();
|
||||
|
||||
void forceUpdate();
|
||||
|
||||
Wallet.ConnectionStatus getConnectionStatus();
|
||||
|
||||
long getDaemonHeight(); //mBoundService.getDaemonHeight();
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
void onSendRequest();
|
||||
|
||||
void onTxDetailsRequest(TransactionInfo info);
|
||||
|
||||
boolean isSynced();
|
||||
|
||||
boolean isWatchOnly();
|
||||
|
||||
String getTxKey(String txId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Listener) {
|
||||
this.activityCallback = (Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.layout;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -22,13 +38,15 @@ import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfoAdapter.ViewHolder> {
|
||||
static final String TAG = "TransactionInfoAdapter";
|
||||
private static final String TAG = "TransactionInfoAdapter";
|
||||
|
||||
static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
|
||||
static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
|
||||
private final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
static final int TX_RED = Color.rgb(255, 79, 65);
|
||||
static final int TX_GREEN = Color.rgb(54, 176, 91);
|
||||
static final int TX_PENDING = Color.rgb(72, 53, 176);
|
||||
static final int TX_FAILED = Color.rgb(208, 0, 255);
|
||||
|
||||
public interface OnInteractionListener {
|
||||
void onInteraction(View view, TransactionInfo item);
|
||||
@@ -65,6 +83,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
|
||||
public void setInfos(List<TransactionInfo> data) {
|
||||
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
|
||||
// as the TransactionInfo items are always recreated, we cannot recycle
|
||||
this.infoItems.clear();
|
||||
if (data != null) {
|
||||
Log.d(TAG, "setInfos " + data.size());
|
||||
@@ -72,8 +91,15 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
Collections.sort(data, new Comparator<TransactionInfo>() {
|
||||
@Override
|
||||
public int compare(TransactionInfo o1, TransactionInfo o2) {
|
||||
long b1 = o1.getBlockHeight();
|
||||
long b2 = o2.getBlockHeight();
|
||||
if ((o1.isPending) && (o2.isPending)) {
|
||||
long b1 = o1.timestamp;
|
||||
long b2 = o2.timestamp;
|
||||
return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
|
||||
}
|
||||
if (o1.isPending) return -1;
|
||||
if (o2.isPending) return 1;
|
||||
long b1 = o1.blockheight;
|
||||
long b2 = o2.blockheight;
|
||||
return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
|
||||
}
|
||||
});
|
||||
@@ -84,15 +110,15 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
public final TextView tvAmount;
|
||||
public final TextView tvAmountPoint;
|
||||
public final TextView tvAmountDecimal;
|
||||
public final TextView tvDate;
|
||||
public final TextView tvTime;
|
||||
public TransactionInfo infoItem;
|
||||
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
final TextView tvAmount;
|
||||
final TextView tvAmountPoint;
|
||||
final TextView tvAmountDecimal;
|
||||
final TextView tvDate;
|
||||
final TextView tvTime;
|
||||
TransactionInfo infoItem;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
this.tvAmount = (TextView) itemView.findViewById(R.id.tx_amount);
|
||||
// I know this is stupid but can't be bothered to align decimals otherwise
|
||||
@@ -118,20 +144,28 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
|
||||
void bind(int position) {
|
||||
this.infoItem = infoItems.get(position);
|
||||
String displayAmount = Wallet.getDisplayAmount(infoItem.getAmount());
|
||||
// TODO fix this with i8n code
|
||||
String displayAmount = Wallet.getDisplayAmount(infoItem.amount);
|
||||
// TODO fix this with i8n code but cryptonote::print_money always uses '.' for decimal point
|
||||
String amountParts[] = displayAmount.split("\\.");
|
||||
// TODO what if there is no decimal point?
|
||||
|
||||
this.tvAmount.setText(amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1]);
|
||||
if (infoItem.getDirection() == TransactionInfo.Direction.Direction_In) {
|
||||
if (infoItem.isPending) {
|
||||
setTxColour(TX_PENDING);
|
||||
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
|
||||
this.tvAmount.setText('-' + amountParts[0]);
|
||||
}
|
||||
} else if (infoItem.isFailed) {
|
||||
this.tvAmount.setText('(' + amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1] + ')');
|
||||
setTxColour(TX_FAILED);
|
||||
} else if (infoItem.direction == TransactionInfo.Direction.Direction_In) {
|
||||
setTxColour(TX_GREEN);
|
||||
} else {
|
||||
setTxColour(TX_RED);
|
||||
}
|
||||
this.tvDate.setText(getDate(infoItem.getTimestamp()));
|
||||
this.tvTime.setText(getTime(infoItem.getTimestamp()));
|
||||
this.tvDate.setText(getDate(infoItem.timestamp));
|
||||
this.tvTime.setText(getTime(infoItem.timestamp));
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
public class PendingTransaction {
|
||||
static {
|
||||
System.loadLibrary("monerujo");
|
||||
}
|
||||
|
||||
public long handle;
|
||||
|
||||
PendingTransaction(long handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
Status_Ok,
|
||||
Status_Error,
|
||||
Status_Critical
|
||||
}
|
||||
|
||||
public enum Priority {
|
||||
Priority_Low(1),
|
||||
Priority_Medium(2),
|
||||
Priority_High(3),
|
||||
Priority_Last(4);
|
||||
|
||||
public static Priority fromInteger(int n) {
|
||||
switch (n) {
|
||||
case 1:
|
||||
return Priority_Low;
|
||||
case 2:
|
||||
return Priority_Medium;
|
||||
case 3:
|
||||
return Priority_High;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private int value;
|
||||
|
||||
Priority(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return Status.values()[getStatusJ()];
|
||||
}
|
||||
|
||||
public native int getStatusJ();
|
||||
|
||||
public native String getErrorString();
|
||||
|
||||
// commit transaction or save to file if filename is provided.
|
||||
public native boolean commit(String filename, boolean overwrite);
|
||||
|
||||
public native long getAmount();
|
||||
|
||||
public native long getDust();
|
||||
|
||||
public native long getFee();
|
||||
|
||||
public native String getFirstTxId();
|
||||
|
||||
public native long getTxCount();
|
||||
|
||||
}
|
@@ -1,5 +1,22 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TransactionHistory {
|
||||
@@ -13,34 +30,22 @@ public class TransactionHistory {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public TransactionInfo getTransaction(int i) {
|
||||
long infoHandle = getTransactionByIndexJ(i);
|
||||
return new TransactionInfo(infoHandle);
|
||||
}
|
||||
|
||||
public TransactionInfo getTransaction(String id) {
|
||||
long infoHandle = getTransactionByIdJ(id);
|
||||
return new TransactionInfo(infoHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
public List<TransactionInfo> getAll() {
|
||||
List<Long> handles = getAllJ();
|
||||
List<TransactionInfo> infoList = new ArrayList<TransactionInfo>();
|
||||
for (Long handle : handles) {
|
||||
infoList.add(new TransactionInfo(handle.longValue()));
|
||||
}
|
||||
return infoList;
|
||||
}
|
||||
*/
|
||||
public native int getCount();
|
||||
|
||||
private native long getTransactionByIndexJ(int i);
|
||||
//private native long getTransactionByIndexJ(int i);
|
||||
|
||||
private native long getTransactionByIdJ(String id);
|
||||
//private native long getTransactionByIdJ(String id);
|
||||
|
||||
public native List<TransactionInfo> getAll();
|
||||
public List<TransactionInfo> getAll() {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public native void refresh();
|
||||
private List<TransactionInfo> transactions = new ArrayList<>();
|
||||
|
||||
public void refresh() {
|
||||
transactions = refreshJ();
|
||||
}
|
||||
|
||||
private native List<TransactionInfo> refreshJ();
|
||||
|
||||
}
|
||||
|
@@ -1,78 +1,149 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
public class TransactionInfo {
|
||||
static {
|
||||
System.loadLibrary("monerujo");
|
||||
}
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
public long handle;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public TransactionInfo(long handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
// this is not the TransactionInfo from the API as that is owned by the TransactionHistory
|
||||
// this is a POJO for the TransactionInfoAdapter
|
||||
public class TransactionInfo implements Parcelable {
|
||||
static final String TAG = "TransactionInfo";
|
||||
|
||||
public enum Direction {
|
||||
Direction_In,
|
||||
Direction_Out
|
||||
}
|
||||
Direction_In(0),
|
||||
Direction_Out(1);
|
||||
|
||||
public class Transfer {
|
||||
long amount;
|
||||
String address;
|
||||
|
||||
public Transfer(long amount, String address) {
|
||||
this.amount = amount;
|
||||
this.address = address;
|
||||
public static Direction fromInteger(int n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return Direction_In;
|
||||
case 1:
|
||||
return Direction_Out;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getAmount() {
|
||||
return amount;
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
private int value;
|
||||
|
||||
Direction(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Direction direction;
|
||||
public boolean isPending;
|
||||
public boolean isFailed;
|
||||
public long amount;
|
||||
public long fee;
|
||||
public long blockheight;
|
||||
public String hash;
|
||||
public long timestamp;
|
||||
public String paymentId;
|
||||
public long confirmations;
|
||||
public List<Transfer> transfers;
|
||||
|
||||
public String txKey = null;
|
||||
public String notes = null;
|
||||
|
||||
public TransactionInfo(
|
||||
int direction,
|
||||
boolean isPending,
|
||||
boolean isFailed,
|
||||
long amount,
|
||||
long fee,
|
||||
long blockheight,
|
||||
String hash,
|
||||
long timestamp,
|
||||
String paymentId,
|
||||
long confirmations,
|
||||
List<Transfer> transfers) {
|
||||
this.direction = Direction.values()[direction];
|
||||
this.isPending = isPending;
|
||||
this.isFailed = isFailed;
|
||||
this.amount = amount;
|
||||
this.fee = fee;
|
||||
this.blockheight = blockheight;
|
||||
this.hash = hash;
|
||||
this.timestamp = timestamp;
|
||||
this.paymentId = paymentId;
|
||||
this.confirmations = confirmations;
|
||||
this.transfers = transfers;
|
||||
}
|
||||
Random rnd = new Random();
|
||||
|
||||
public String toString() {
|
||||
return getDirection() + "@" + getBlockHeight() + " " + getAmount();
|
||||
return direction + "@" + blockheight + " " + amount;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return TransactionInfo.Direction.values()[getDirectionJ()];
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(direction.getValue());
|
||||
out.writeByte((byte) (isPending ? 1 : 0));
|
||||
out.writeByte((byte) (isFailed ? 1 : 0));
|
||||
out.writeLong(amount);
|
||||
out.writeLong(fee);
|
||||
out.writeLong(blockheight);
|
||||
out.writeString(hash);
|
||||
out.writeLong(timestamp);
|
||||
out.writeString(paymentId);
|
||||
out.writeLong(confirmations);
|
||||
out.writeList(transfers);
|
||||
out.writeString(txKey);
|
||||
out.writeString(notes);
|
||||
}
|
||||
|
||||
public native int getDirectionJ();
|
||||
|
||||
public native boolean isPending();
|
||||
|
||||
public native boolean isFailed();
|
||||
|
||||
public native long getAmount();
|
||||
|
||||
public native long getFee();
|
||||
|
||||
public native long getBlockHeight();
|
||||
|
||||
public native long getConfirmations();
|
||||
|
||||
public native String getHash();
|
||||
|
||||
public native long getTimestamp();
|
||||
|
||||
public native String getPaymentId();
|
||||
|
||||
/*
|
||||
private List<Transfer> transfers;
|
||||
|
||||
public List<Transfer> getTransfers() { // not threadsafe
|
||||
if (this.transfers == null) {
|
||||
this.transfers = getTransfersJ();
|
||||
public static final Parcelable.Creator<TransactionInfo> CREATOR = new Parcelable.Creator<TransactionInfo>() {
|
||||
public TransactionInfo createFromParcel(Parcel in) {
|
||||
return new TransactionInfo(in);
|
||||
}
|
||||
return this.transfers;
|
||||
|
||||
public TransactionInfo[] newArray(int size) {
|
||||
return new TransactionInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
private TransactionInfo(Parcel in) {
|
||||
direction = Direction.fromInteger(in.readInt());
|
||||
isPending = in.readByte() != 0;
|
||||
isFailed = in.readByte() != 0;
|
||||
amount = in.readLong();
|
||||
fee = in.readLong();
|
||||
blockheight = in.readLong();
|
||||
hash = in.readString();
|
||||
timestamp = in.readLong();
|
||||
paymentId = in.readString();
|
||||
confirmations = in.readLong();
|
||||
transfers = in.readArrayList(Transfer.class.getClassLoader());
|
||||
txKey = in.readString();
|
||||
notes = in.readString();
|
||||
}
|
||||
|
||||
private native List<Transfer> getTransfersJ();
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
59
app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java
Normal file
59
app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.m2049r.xmrwallet.util.TxData;
|
||||
|
||||
public class Transfer implements Parcelable {
|
||||
public long amount;
|
||||
public String address;
|
||||
|
||||
public Transfer(long amount, String address) {
|
||||
this.amount = amount;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeLong(amount);
|
||||
out.writeString(address);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Transfer> CREATOR = new Parcelable.Creator<Transfer>() {
|
||||
public Transfer createFromParcel(Parcel in) {
|
||||
return new Transfer(in);
|
||||
}
|
||||
|
||||
public Transfer[] newArray(int size) {
|
||||
return new Transfer[size];
|
||||
}
|
||||
};
|
||||
|
||||
private Transfer(Parcel in) {
|
||||
amount = in.readLong();
|
||||
address = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +1,25 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Wallet {
|
||||
static {
|
||||
System.loadLibrary("monerujo");
|
||||
@@ -9,10 +27,14 @@ public class Wallet {
|
||||
|
||||
static final String TAG = "Wallet";
|
||||
|
||||
public String getName() {
|
||||
return new File(getPath()).getName();
|
||||
}
|
||||
|
||||
private long handle = 0;
|
||||
private long listenerHandle = 0;
|
||||
|
||||
public Wallet(long handle) {
|
||||
Wallet(long handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
@@ -30,8 +52,6 @@ public class Wallet {
|
||||
|
||||
//public native long createWalletListenerJ();
|
||||
|
||||
public native boolean close(); // from WalletManager
|
||||
|
||||
public native String getSeed();
|
||||
|
||||
public native String getSeedLanguage();
|
||||
@@ -62,15 +82,25 @@ public class Wallet {
|
||||
public native String getSecretViewKey();
|
||||
|
||||
public boolean store() {
|
||||
return store(this.getPath());
|
||||
return store("");
|
||||
}
|
||||
|
||||
public native boolean store(String path);
|
||||
|
||||
public boolean close() {
|
||||
disposePendingTransaction();
|
||||
return WalletManager.getInstance().close(this);
|
||||
}
|
||||
|
||||
public native String getFilename();
|
||||
|
||||
// virtual std::string keysFilename() const = 0;
|
||||
public native boolean init(String daemon_address, long upper_transaction_size_limit);
|
||||
public boolean init(long upper_transaction_size_limit) {
|
||||
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit);
|
||||
}
|
||||
|
||||
private native boolean initJ(String daemon_address, long upper_transaction_size_limit);
|
||||
|
||||
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
|
||||
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
|
||||
// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
|
||||
@@ -132,15 +162,47 @@ public class Wallet {
|
||||
//TODO virtual int autoRefreshInterval() const = 0;
|
||||
|
||||
|
||||
//virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
||||
// optional<uint64_t> tvAmount, uint32_t mixin_count,
|
||||
// PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0;
|
||||
private PendingTransaction pendingTransaction = null;
|
||||
|
||||
//virtual PendingTransaction * createSweepUnmixableTransaction() = 0;
|
||||
public PendingTransaction getPendingTransaction() {
|
||||
return pendingTransaction;
|
||||
}
|
||||
|
||||
public void disposePendingTransaction() {
|
||||
if (pendingTransaction != null) {
|
||||
disposeTransaction(pendingTransaction);
|
||||
pendingTransaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
public PendingTransaction createTransaction(String dst_addr, String payment_id,
|
||||
long amount, int mixin_count,
|
||||
PendingTransaction.Priority priority) {
|
||||
disposePendingTransaction();
|
||||
long txHandle = createTransactionJ(dst_addr, payment_id, amount, mixin_count, priority);
|
||||
pendingTransaction = new PendingTransaction(txHandle);
|
||||
return pendingTransaction;
|
||||
}
|
||||
|
||||
private native long createTransactionJ(String dst_addr, String payment_id,
|
||||
long mount, int mixin_count,
|
||||
PendingTransaction.Priority priority);
|
||||
|
||||
|
||||
public PendingTransaction createSweepUnmixableTransaction() {
|
||||
disposePendingTransaction();
|
||||
long txHandle = createSweepUnmixableTransactionJ();
|
||||
pendingTransaction = new PendingTransaction(txHandle);
|
||||
return pendingTransaction;
|
||||
}
|
||||
|
||||
private native long createSweepUnmixableTransactionJ();
|
||||
|
||||
//virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0;
|
||||
//virtual bool submitTransaction(const std::string &fileName) = 0;
|
||||
//virtual void disposeTransaction(PendingTransaction * t) = 0;
|
||||
|
||||
public native void disposeTransaction(PendingTransaction pendingTransaction);
|
||||
|
||||
//virtual bool exportKeyImages(const std::string &filename) = 0;
|
||||
//virtual bool importKeyImages(const std::string &filename) = 0;
|
||||
|
||||
@@ -171,9 +233,10 @@ public class Wallet {
|
||||
|
||||
public native void setDefaultMixin(int mixin);
|
||||
|
||||
//virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0;
|
||||
//virtual std::string getUserNote(const std::string &txid) const = 0;
|
||||
//virtual std::string getTxKey(const std::string &txid) const = 0;
|
||||
public native boolean setUserNote(String txid, String note);
|
||||
public native String getUserNote(String txid);
|
||||
|
||||
public native String getTxKey(String txid);
|
||||
|
||||
//virtual std::string signMessage(const std::string &message) = 0;
|
||||
//virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
|
||||
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
public interface WalletListener {
|
||||
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import android.util.Log;
|
||||
@@ -8,10 +24,12 @@ import java.io.FileReader;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WalletManager {
|
||||
final static String TAG = "WalletManager";
|
||||
private final static String TAG = "WalletManager";
|
||||
|
||||
static {
|
||||
System.loadLibrary("monerujo");
|
||||
@@ -20,38 +38,81 @@ public class WalletManager {
|
||||
// no need to keep a reference to the REAL WalletManager (we get it every tvTime we need it)
|
||||
private static WalletManager Instance = null;
|
||||
|
||||
public static WalletManager getInstance() { // TODO not threadsafe
|
||||
public static synchronized WalletManager getInstance() {
|
||||
if (WalletManager.Instance == null) {
|
||||
WalletManager.Instance = new WalletManager();
|
||||
}
|
||||
return WalletManager.Instance;
|
||||
}
|
||||
|
||||
public Wallet createWallet(String path, String password, String language, boolean isTestNet) {
|
||||
long walletHandle = createWalletJ(path, password, language, isTestNet);
|
||||
return new Wallet(walletHandle);
|
||||
private WalletManager() {
|
||||
this.managedWallets = new HashMap<>();
|
||||
}
|
||||
|
||||
public Wallet openWallet(String path, String password, boolean isTestNet) {
|
||||
long walletHandle = openWalletJ(path, password, isTestNet);
|
||||
return new Wallet(walletHandle);
|
||||
private Map<String, Wallet> managedWallets;
|
||||
|
||||
public Wallet getWallet(String walletId) {
|
||||
return managedWallets.get(walletId);
|
||||
}
|
||||
|
||||
public Wallet recoveryWallet(String path, String mnemonic, boolean isTestNet) {
|
||||
return recoveryWallet(path, mnemonic, isTestNet, 0);
|
||||
private void manageWallet(String walletId, Wallet wallet) {
|
||||
if (getWallet(walletId) != null) {
|
||||
throw new IllegalStateException(walletId + " already under management!");
|
||||
}
|
||||
Log.d(TAG, "Managing " + walletId);
|
||||
managedWallets.put(walletId, wallet);
|
||||
}
|
||||
|
||||
public Wallet recoveryWallet(String path, String mnemonic, boolean isTestNet, long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet, restoreHeight);
|
||||
return new Wallet(walletHandle);
|
||||
private void unmanageWallet(String walletId) {
|
||||
if (getWallet(walletId) == null) {
|
||||
throw new IllegalStateException(walletId + " not under management!");
|
||||
}
|
||||
Log.d(TAG, "Unmanaging " + walletId);
|
||||
managedWallets.remove(walletId);
|
||||
}
|
||||
|
||||
public Wallet createWallet(String path, String password, String language) {
|
||||
long walletHandle = createWalletJ(path, password, language, isTestNet());
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet.getName(), wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long createWalletJ(String path, String password, String language, boolean isTestNet);
|
||||
|
||||
public Wallet openWallet(String path, String password) {
|
||||
long walletHandle = openWalletJ(path, password, isTestNet());
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet.getName(), wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long openWalletJ(String path, String password, boolean isTestNet);
|
||||
|
||||
public Wallet recoveryWallet(String path, String mnemonic) {
|
||||
Wallet wallet = recoveryWallet(path, mnemonic, 0);
|
||||
manageWallet(wallet.getName(), wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public Wallet recoveryWallet(String path, String mnemonic, long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet(), restoreHeight);
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet.getName(), wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight);
|
||||
|
||||
public Wallet createWalletFromKeys(String path, String language, long restoreHeight,
|
||||
String addressString, String viewKeyString, String spendKeyString) {
|
||||
long walletHandle = createWalletFromKeysJ(path, language, isTestNet(), restoreHeight,
|
||||
addressString, viewKeyString, spendKeyString);
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet.getName(), wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long createWalletFromKeysJ(String path, String language,
|
||||
boolean isTestNet,
|
||||
long restoreHeight,
|
||||
@@ -59,6 +120,20 @@ public class WalletManager {
|
||||
String viewKeyString,
|
||||
String spendKeyString);
|
||||
|
||||
public native boolean closeJ(Wallet wallet);
|
||||
|
||||
public boolean close(Wallet wallet) {
|
||||
String walletId = new File(wallet.getFilename()).getName();
|
||||
unmanageWallet(walletId);
|
||||
boolean closed = closeJ(wallet);
|
||||
if (!closed) {
|
||||
// in case we could not close it
|
||||
// we unmanage it
|
||||
manageWallet(walletId, wallet);
|
||||
}
|
||||
return closed;
|
||||
}
|
||||
|
||||
public native boolean walletExists(String path);
|
||||
|
||||
public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only);
|
||||
@@ -85,7 +160,7 @@ public class WalletManager {
|
||||
String filename = found[i].getName();
|
||||
info.name = filename.substring(0, filename.length() - 5); // 5 is length of ".keys"+1
|
||||
File addressFile = new File(path, info.name + ".address.txt");
|
||||
Log.d(TAG, addressFile.getAbsolutePath());
|
||||
//Log.d(TAG, addressFile.getAbsolutePath());
|
||||
info.address = "??????";
|
||||
BufferedReader addressReader = null;
|
||||
try {
|
||||
@@ -111,20 +186,34 @@ 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;
|
||||
|
||||
String daemonAddress = null;
|
||||
private String daemonAddress = null;
|
||||
private boolean testnet = true;
|
||||
|
||||
public void setDaemonAddress(String address) {
|
||||
public boolean isTestNet() {
|
||||
if (daemonAddress == null) {
|
||||
// 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) {
|
||||
this.daemonAddress = address;
|
||||
this.testnet = testnet;
|
||||
setDaemonAddressJ(address);
|
||||
}
|
||||
|
||||
public String getDaemonAddress() {
|
||||
if (daemonAddress == null) {
|
||||
// assume testnet not explicitly initialised
|
||||
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
|
||||
}
|
||||
return this.daemonAddress;
|
||||
}
|
||||
|
||||
private native void setDaemonAddressJ(String address);
|
||||
|
||||
public native int getConnectedDaemonVersion();
|
||||
public native int getDaemonVersion();
|
||||
|
||||
public native long getBlockchainHeight();
|
||||
|
||||
|
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
* 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.service;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
|
||||
|
||||
/**
|
||||
* Handy class for starting a new thread that has a looper. The looper can then be
|
||||
* used to create handler classes. Note that start() must still be called.
|
||||
* The started Thread has a stck size of STACK_SIZE (=5MB)
|
||||
*/
|
||||
public class MoneroHandlerThread extends Thread {
|
||||
// from src/cryptonote_config.h
|
||||
static public final long THREAD_STACK_SIZE = 5 * 1024 * 1024;
|
||||
int mPriority;
|
||||
int mTid = -1;
|
||||
Looper mLooper;
|
||||
|
||||
public MoneroHandlerThread(String name) {
|
||||
super(null, null, name, THREAD_STACK_SIZE);
|
||||
mPriority = Process.THREAD_PRIORITY_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MoneroHandlerThread.
|
||||
*
|
||||
* @param name
|
||||
* @param priority The priority to run the thread at. The value supplied must be from
|
||||
* {@link android.os.Process} and not from java.lang.Thread.
|
||||
*/
|
||||
public MoneroHandlerThread(String name, int priority) {
|
||||
super(null, null, name, THREAD_STACK_SIZE);
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call back method that can be explicitly overridden if needed to execute some
|
||||
* setup before Looper loops.
|
||||
*/
|
||||
|
||||
protected void onLooperPrepared() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mTid = Process.myTid();
|
||||
Looper.prepare();
|
||||
synchronized (this) {
|
||||
mLooper = Looper.myLooper();
|
||||
notifyAll();
|
||||
}
|
||||
Process.setThreadPriority(mPriority);
|
||||
onLooperPrepared();
|
||||
Looper.loop();
|
||||
mTid = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the Looper associated with this thread. If this thread not been started
|
||||
* or for any reason is isAlive() returns false, this method will return null. If this thread
|
||||
* has been started, this method will block until the looper has been initialized.
|
||||
*
|
||||
* @return The looper.
|
||||
*/
|
||||
public Looper getLooper() {
|
||||
if (!isAlive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the thread has been started, wait until the looper has been created.
|
||||
synchronized (this) {
|
||||
while (isAlive() && mLooper == null) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return mLooper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quits the handler thread's looper.
|
||||
* <p>
|
||||
* Causes the handler thread's looper to terminate without processing any
|
||||
* more messages in the message queue.
|
||||
* </p><p>
|
||||
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
|
||||
* For example, the {@link Handler#sendMessage(Message)} method will return false.
|
||||
* </p><p class="note">
|
||||
* Using this method may be unsafe because some messages may not be delivered
|
||||
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
|
||||
* that all pending work is completed in an orderly manner.
|
||||
* </p>
|
||||
*
|
||||
* @return True if the looper looper has been asked to quit or false if the
|
||||
* thread had not yet started running.
|
||||
* @see #quitSafely
|
||||
*/
|
||||
public boolean quit() {
|
||||
Looper looper = getLooper();
|
||||
if (looper != null) {
|
||||
looper.quit();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quits the handler thread's looper safely.
|
||||
* <p>
|
||||
* Causes the handler thread's looper to terminate as soon as all remaining messages
|
||||
* in the message queue that are already due to be delivered have been handled.
|
||||
* Pending delayed messages with due times in the future will not be delivered.
|
||||
* </p><p>
|
||||
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
|
||||
* For example, the {@link Handler#sendMessage(Message)} method will return false.
|
||||
* </p><p>
|
||||
* If the thread has not been started or has finished (that is if
|
||||
* {@link #getLooper} returns null), then false is returned.
|
||||
* Otherwise the looper is asked to quit and true is returned.
|
||||
* </p>
|
||||
*
|
||||
* @return True if the looper looper has been asked to quit or false if the
|
||||
* thread had not yet started running.
|
||||
*/
|
||||
public boolean quitSafely() {
|
||||
Looper looper = getLooper();
|
||||
if (looper != null) {
|
||||
looper.quitSafely();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of this thread. See Process.myTid().
|
||||
*/
|
||||
public int getThreadId() {
|
||||
return mTid;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,38 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Helper {
|
||||
static final String TAG = "Helper";
|
||||
static final String WALLET_DIR = "Monerujo";
|
||||
private static final String TAG = "Helper";
|
||||
private static final String WALLET_DIR = "Monerujo";
|
||||
|
||||
static public File getStorageRoot(Context context) {
|
||||
if (!isExternalStorageWritable()) {
|
||||
@@ -27,7 +46,7 @@ public class Helper {
|
||||
dir.mkdirs(); // try to make it
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
String msg = "Directory " + dir.getAbsolutePath() + " does not exists.";
|
||||
String msg = "Directory " + dir.getAbsolutePath() + " does not exist.";
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
@@ -40,7 +59,7 @@ public class Helper {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_DENIED) {
|
||||
Log.d("permission", "permission denied to WRITE_EXTERNAL_STORAGE - requesting it");
|
||||
Log.d(TAG, "Permission denied to WRITE_EXTERNAL_STORAGE - requesting it");
|
||||
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
return false;
|
||||
@@ -54,11 +73,7 @@ public class Helper {
|
||||
|
||||
static public String getWalletPath(Context context, String aWalletName) {
|
||||
File walletDir = getStorageRoot(context);
|
||||
Log.d(TAG, "walletdir=" + walletDir.getAbsolutePath());
|
||||
if (!walletDir.exists()) {
|
||||
Log.d(TAG, "walletdir did not exist!");
|
||||
walletDir.mkdirs();
|
||||
}
|
||||
//d(TAG, "walletdir=" + walletDir.getAbsolutePath());
|
||||
File f = new File(walletDir, aWalletName);
|
||||
Log.d(TAG, "wallet = " + f.getAbsolutePath() + " size=" + f.length());
|
||||
return f.getAbsolutePath();
|
||||
@@ -67,10 +82,29 @@ public class Helper {
|
||||
/* Checks if external storage is available for read and write */
|
||||
static public boolean isExternalStorageWritable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
static public void showKeyboard(Activity act) {
|
||||
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(act.getCurrentFocus(), InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
static public void hideKeyboard(Activity act) {
|
||||
if (act.getCurrentFocus() == null) {
|
||||
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
|
||||
} else {
|
||||
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow((null == act.getCurrentFocus()) ? null : act.getCurrentFocus().getWindowToken(),
|
||||
InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
static public void showKeyboard(Dialog dialog) {
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
}
|
||||
|
||||
static public void hideKeyboardAlways(Activity act) {
|
||||
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
78
app/src/main/java/com/m2049r/xmrwallet/util/TxData.java
Normal file
78
app/src/main/java/com/m2049r/xmrwallet/util/TxData.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
|
||||
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
||||
public class TxData implements Parcelable {
|
||||
public TxData(String dst_addr,
|
||||
String paymentId,
|
||||
long amount,
|
||||
int mixin,
|
||||
PendingTransaction.Priority priority) {
|
||||
this.dst_addr = dst_addr;
|
||||
this.paymentId = paymentId;
|
||||
this.amount = amount;
|
||||
this.mixin = mixin;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String dst_addr;
|
||||
public String paymentId;
|
||||
public long amount;
|
||||
public int mixin;
|
||||
public PendingTransaction.Priority priority;
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(dst_addr);
|
||||
out.writeString(paymentId);
|
||||
out.writeLong(amount);
|
||||
out.writeInt(mixin);
|
||||
out.writeInt(priority.getValue());
|
||||
}
|
||||
|
||||
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
|
||||
public static final Parcelable.Creator<TxData> CREATOR = new Parcelable.Creator<TxData>() {
|
||||
public TxData createFromParcel(Parcel in) {
|
||||
return new TxData(in);
|
||||
}
|
||||
|
||||
public TxData[] newArray(int size) {
|
||||
return new TxData[size];
|
||||
}
|
||||
};
|
||||
|
||||
private TxData(Parcel in) {
|
||||
dst_addr = in.readString();
|
||||
paymentId = in.readString();
|
||||
amount = in.readLong();
|
||||
mixin = in.readInt();
|
||||
priority = PendingTransaction.Priority.fromInteger(in.readInt());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user