diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8b92c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.gradle +build diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..a7c382e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1 @@ +workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..373175e --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +xmrwallet \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/libraries/animated_vector_drawable_25_3_1.xml b/.idea/libraries/animated_vector_drawable_25_3_1.xml new file mode 100644 index 0000000..78eeffc --- /dev/null +++ b/.idea/libraries/animated_vector_drawable_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/appcompat_v7_25_3_1.xml b/.idea/libraries/appcompat_v7_25_3_1.xml new file mode 100644 index 0000000..f1d4de5 --- /dev/null +++ b/.idea/libraries/appcompat_v7_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/cardview_v7_25_3_1.xml b/.idea/libraries/cardview_v7_25_3_1.xml new file mode 100644 index 0000000..beadd4a --- /dev/null +++ b/.idea/libraries/cardview_v7_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/constraint_layout_1_0_2.xml b/.idea/libraries/constraint_layout_1_0_2.xml new file mode 100644 index 0000000..ae7ec8d --- /dev/null +++ b/.idea/libraries/constraint_layout_1_0_2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/constraint_layout_solver_1_0_2.xml b/.idea/libraries/constraint_layout_solver_1_0_2.xml new file mode 100644 index 0000000..7525558 --- /dev/null +++ b/.idea/libraries/constraint_layout_solver_1_0_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/design_25_3_1.xml b/.idea/libraries/design_25_3_1.xml new file mode 100644 index 0000000..90e013a --- /dev/null +++ b/.idea/libraries/design_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/espresso_core_2_2_2.xml b/.idea/libraries/espresso_core_2_2_2.xml new file mode 100644 index 0000000..1fe68a2 --- /dev/null +++ b/.idea/libraries/espresso_core_2_2_2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/espresso_idling_resource_2_2_2.xml b/.idea/libraries/espresso_idling_resource_2_2_2.xml new file mode 100644 index 0000000..df91204 --- /dev/null +++ b/.idea/libraries/espresso_idling_resource_2_2_2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml b/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml new file mode 100644 index 0000000..16e1b54 --- /dev/null +++ b/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/hamcrest_core_1_3.xml b/.idea/libraries/hamcrest_core_1_3.xml new file mode 100644 index 0000000..157e3f3 --- /dev/null +++ b/.idea/libraries/hamcrest_core_1_3.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/hamcrest_integration_1_3.xml b/.idea/libraries/hamcrest_integration_1_3.xml new file mode 100644 index 0000000..58b2c4b --- /dev/null +++ b/.idea/libraries/hamcrest_integration_1_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/hamcrest_library_1_3.xml b/.idea/libraries/hamcrest_library_1_3.xml new file mode 100644 index 0000000..676cc63 --- /dev/null +++ b/.idea/libraries/hamcrest_library_1_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/javawriter_2_1_1.xml b/.idea/libraries/javawriter_2_1_1.xml new file mode 100644 index 0000000..a66fefb --- /dev/null +++ b/.idea/libraries/javawriter_2_1_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/javax_annotation_api_1_2.xml b/.idea/libraries/javax_annotation_api_1_2.xml new file mode 100644 index 0000000..811e73f --- /dev/null +++ b/.idea/libraries/javax_annotation_api_1_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/javax_inject_1.xml b/.idea/libraries/javax_inject_1.xml new file mode 100644 index 0000000..0d1d5fc --- /dev/null +++ b/.idea/libraries/javax_inject_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/jsr305_2_0_1.xml b/.idea/libraries/jsr305_2_0_1.xml new file mode 100644 index 0000000..cdf9878 --- /dev/null +++ b/.idea/libraries/jsr305_2_0_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/junit_4_12.xml b/.idea/libraries/junit_4_12.xml new file mode 100644 index 0000000..305df30 --- /dev/null +++ b/.idea/libraries/junit_4_12.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/recyclerview_v7_25_3_1.xml b/.idea/libraries/recyclerview_v7_25_3_1.xml new file mode 100644 index 0000000..6b856b3 --- /dev/null +++ b/.idea/libraries/recyclerview_v7_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/rules_0_5.xml b/.idea/libraries/rules_0_5.xml new file mode 100644 index 0000000..0be4378 --- /dev/null +++ b/.idea/libraries/rules_0_5.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/runner_0_5.xml b/.idea/libraries/runner_0_5.xml new file mode 100644 index 0000000..0d92249 --- /dev/null +++ b/.idea/libraries/runner_0_5.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_annotations_25_3_1.xml b/.idea/libraries/support_annotations_25_3_1.xml new file mode 100644 index 0000000..beb3941 --- /dev/null +++ b/.idea/libraries/support_annotations_25_3_1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_compat_25_3_1.xml b/.idea/libraries/support_compat_25_3_1.xml new file mode 100644 index 0000000..92ae674 --- /dev/null +++ b/.idea/libraries/support_compat_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_core_ui_25_3_1.xml b/.idea/libraries/support_core_ui_25_3_1.xml new file mode 100644 index 0000000..46bcc7f --- /dev/null +++ b/.idea/libraries/support_core_ui_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_core_utils_25_3_1.xml b/.idea/libraries/support_core_utils_25_3_1.xml new file mode 100644 index 0000000..a54f87f --- /dev/null +++ b/.idea/libraries/support_core_utils_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_fragment_25_3_1.xml b/.idea/libraries/support_fragment_25_3_1.xml new file mode 100644 index 0000000..1c87b7a --- /dev/null +++ b/.idea/libraries/support_fragment_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_media_compat_25_3_1.xml b/.idea/libraries/support_media_compat_25_3_1.xml new file mode 100644 index 0000000..03ab57b --- /dev/null +++ b/.idea/libraries/support_media_compat_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_v4_25_3_1.xml b/.idea/libraries/support_v4_25_3_1.xml new file mode 100644 index 0000000..194e0c4 --- /dev/null +++ b/.idea/libraries/support_v4_25_3_1.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/support_vector_drawable_25_3_1.xml b/.idea/libraries/support_vector_drawable_25_3_1.xml new file mode 100644 index 0000000..c4a86f4 --- /dev/null +++ b/.idea/libraries/support_vector_drawable_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/transition_25_3_1.xml b/.idea/libraries/transition_25_3_1.xml new file mode 100644 index 0000000..0cf2b5a --- /dev/null +++ b/.idea/libraries/transition_25_3_1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5d19981 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..dce0d43 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd0557e --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Monerujo +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) +- see the [FAQ](doc/FAQ.md) + +### Disclaimer +This is my first serious Android 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! +- currently only android32 +- sorry for my messups in github +- 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) +- use your own daemon - it's easy +- screen stays on until first sync is complete + +### TODO +- make it pretty +- adjust layout so we can use bigger font sizes +- provide detailed build instructions for third party binaries +- visibility of methods/classes +- 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 +- License Dialog + +### Issues +- screen rotation crashes the app +- turning the display off/on during sync stops sync + +### HOW TO BUILD +No need to build. Binaries are included: + +- openssl-1.0.2l +- monero-v0.10.3.1 + pull requests #2238 & #2239 +- boost_1_64_0 + +If you want to build - fire up Android Studio and build. Also you can rebuild all of the above. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..e783827 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +.externalNativeBuild +build diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..d971b42 --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,172 @@ +cmake_minimum_required(VERSION 3.4.1) +message(STATUS ABI_INFO = ${ANDROID_ABI}) + +add_library( monerujo + SHARED + src/main/cpp/monerujo.cpp ) + +set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs) + +############ +# OpenSSL +############ + +add_library(crypto STATIC IMPORTED) +set_target_properties(crypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a) + +add_library(ssl STATIC IMPORTED) +set_target_properties(ssl PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a) + +############ +# Boost +############ + +add_library(boost_chrono STATIC IMPORTED) +set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_chrono.a) + +add_library(boost_date_time STATIC IMPORTED) +set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_date_time.a) + +add_library(boost_filesystem STATIC IMPORTED) +set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_filesystem.a) + +add_library(boost_program_options STATIC IMPORTED) +set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_program_options.a) + +add_library(boost_regex STATIC IMPORTED) +set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_regex.a) + +add_library(boost_serialization STATIC IMPORTED) +set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_serialization.a) + +add_library(boost_system STATIC IMPORTED) +set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_system.a) + +add_library(boost_thread STATIC IMPORTED) +set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_thread.a) + +add_library(boost_wserialization STATIC IMPORTED) +set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a) + +############# +# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct) +############# + +add_library(wallet STATIC IMPORTED) +set_target_properties(wallet PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a) + +add_library(cryptonote_core STATIC IMPORTED) +set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a) + +add_library(cryptonote_basic STATIC IMPORTED) +set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a) + +add_library(mnemonics STATIC IMPORTED) +set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a) + +add_library(common STATIC IMPORTED) +set_target_properties(common PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a) + +add_library(cncrypto STATIC IMPORTED) +set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a) + +add_library(ringct STATIC IMPORTED) +set_target_properties(ringct PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a) + +##### + +add_library(p2p STATIC IMPORTED) +set_target_properties(p2p PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a) + +add_library(blockchain_db STATIC IMPORTED) +set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a) + +add_library(lmdb STATIC IMPORTED) +set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a) + +add_library(easylogging STATIC IMPORTED) +set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a) + +add_library(unbound STATIC IMPORTED) +set_target_properties(unbound PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a) + +#### +add_library(epee STATIC IMPORTED) +set_target_properties(epee PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a) + +add_library(blocks STATIC IMPORTED) +set_target_properties(blocks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a) + +add_library(miniupnpc STATIC IMPORTED) +set_target_properties(miniupnpc PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libminiupnpc.a) + +############# +# System +############# + +find_library( log-lib log ) + +include_directories( ${EXTERNAL_LIBS_DIR}/monero/include ) + +message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) + +target_link_libraries( monerujo + wallet + cryptonote_core + cryptonote_basic + mnemonics + ringct + common + cncrypto + + blockchain_db + lmdb + #easylogging # not for 0.10.3.1 + unbound + p2p + + epee + blocks + miniupnpc + + boost_chrono + boost_date_time + boost_filesystem + boost_program_options + boost_regex + boost_serialization + boost_system + boost_thread + boost_wserialization + + ssl + crypto + + ${log-lib} + ) diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..b6bdf79 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..096647e --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,50 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.2" + defaultConfig { + applicationId "com.m2049r.xmrwallet" + minSdkVersion 21 + targetSdkVersion 25 + versionCode 2 + versionName "0.2.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags "-std=c++11" + arguments '-DANDROID_STL=c++_shared' + } + } + ndk { + abiFilters 'armeabi-v7a' + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:design:25.3.1' + compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.android.support:support-v4:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' + testCompile 'junit:junit:4.12' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..cb4574a --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\Test\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..92f895a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp new file mode 100644 index 0000000..a5eda8a --- /dev/null +++ b/app/src/main/cpp/monerujo.cpp @@ -0,0 +1,1004 @@ +/** + * 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. + */ + +#include +#include "monerujo.h" +#include "wallet2_api.h" + +//TODO explicit casting jlong, jint, jboolean to avoid warnings + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#define LOG_TAG "WalletNDK" +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,__VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG,__VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__) + +static JavaVM *cachedJVM; +static jclass class_ArrayList; +static jclass class_WalletListener; +static jclass class_TransactionInfo; +static jclass class_TransactionInfo$Transfer; + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + cachedJVM = jvm; + LOGI("JNI_OnLoad"); + JNIEnv *jenv; + if (jvm->GetEnv(reinterpret_cast(&jenv), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + //LOGI("JNI_OnLoad ok"); + + class_ArrayList = static_cast(jenv->NewGlobalRef( + jenv->FindClass("java/util/ArrayList"))); + class_TransactionInfo = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo"))); + class_TransactionInfo$Transfer = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo$Transfer"))); + class_WalletListener = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener"))); + return JNI_VERSION_1_6; +} +#ifdef __cplusplus +} +#endif + +int attachJVM(JNIEnv **jenv) { + int envStat = cachedJVM->GetEnv((void **) jenv, JNI_VERSION_1_6); + if (envStat == JNI_EDETACHED) { + if (cachedJVM->AttachCurrentThread(jenv, nullptr) != 0) { + LOGE("Failed to attach"); + return JNI_ERR; + } + } else if (envStat == JNI_EVERSION) { + LOGE("GetEnv: version not supported"); + return JNI_ERR; + } + //LOGI("envStat=%i", envStat); + return envStat; +} + +void detachJVM(JNIEnv *jenv, int envStat) { + //LOGI("envStat=%i", envStat); + if (jenv->ExceptionCheck()) { + jenv->ExceptionDescribe(); + } + + if (envStat == JNI_EDETACHED) { + cachedJVM->DetachCurrentThread(); + } +} + +struct MyWalletListener : Bitmonero::WalletListener { + jobject jlistener; + + MyWalletListener(JNIEnv *env, jobject aListener) { + LOGD("Created MyListener"); + jlistener = env->NewGlobalRef(aListener);; + } + + ~MyWalletListener() { + LOGD("Destroyed MyListener"); + }; + + void deleteGlobalJavaRef(JNIEnv *env) { + env->DeleteGlobalRef(jlistener); + jlistener = nullptr; + } + + /** + * @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; + */ + void updated() { + if (jlistener == nullptr) return; + LOGD("updated"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jmethodID listenerClass_updated = jenv->GetMethodID(class_WalletListener, "updated", "()V"); + jenv->CallVoidMethod(jlistener, listenerClass_updated); + + detachJVM(jenv, envStat); + } + + + /** + * @brief moneySpent - called when money spent + * @param txId - transaction id + * @param amount - amount + */ + void moneySpent(const std::string &txId, uint64_t amount) { + if (jlistener == nullptr) return; + LOGD("moneySpent %" + PRIu64, amount); + } + + /** + * @brief moneyReceived - called when money received + * @param txId - transaction id + * @param amount - amount + */ + void moneyReceived(const std::string &txId, uint64_t amount) { + if (jlistener == nullptr) return; + LOGD("moneyReceived %" + PRIu64, amount); + } + + /** + * @brief unconfirmedMoneyReceived - called when payment arrived in tx pool + * @param txId - transaction id + * @param amount - amount + */ + void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) { + if (jlistener == nullptr) return; + LOGD("unconfirmedMoneyReceived %" + PRIu64, amount); + } + + /** + * @brief newBlock - called when new block received + * @param height - block height + */ + void newBlock(uint64_t height) { + if (jlistener == nullptr) return; + //LOGD("newBlock"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jlong h = static_cast(height); + jmethodID listenerClass_newBlock = jenv->GetMethodID(class_WalletListener, "newBlock", + "(J)V"); + jenv->CallVoidMethod(jlistener, listenerClass_newBlock, h); + + detachJVM(jenv, envStat); + } + + +/** + * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously + */ + void refreshed() { + if (jlistener == nullptr) return; + LOGD("refreshed"); + JNIEnv *jenv; + + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jmethodID listenerClass_refreshed = jenv->GetMethodID(class_WalletListener, "refreshed", + "()V"); + jenv->CallVoidMethod(jlistener, listenerClass_refreshed); + + detachJVM(jenv, envStat); + } +}; + + +//// helper methods +std::vector java2cpp(JNIEnv *env, jobject arrayList) { + + jmethodID java_util_ArrayList_size = env->GetMethodID(class_ArrayList, "size", "()I"); + jmethodID java_util_ArrayList_get = env->GetMethodID(class_ArrayList, "get", + "(I)Ljava/lang/Object;"); + + jint len = env->CallIntMethod(arrayList, java_util_ArrayList_size); + std::vector result; + result.reserve(len); + for (jint i = 0; i < len; i++) { + jstring element = static_cast(env->CallObjectMethod(arrayList, + java_util_ArrayList_get, i)); + const char *pchars = env->GetStringUTFChars(element, JNI_FALSE); + result.emplace_back(pchars); + env->ReleaseStringUTFChars(element, pchars); + env->DeleteLocalRef(element); + } + return result; +} + +jobject cpp2java(JNIEnv *env, std::vector vector) { + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + + jobject result = env->NewObject(class_ArrayList, java_util_ArrayList_, vector.size()); + for (std::string &s: vector) { + jstring element = env->NewStringUTF(s.c_str()); + env->CallBooleanMethod(result, java_util_ArrayList_add, element); + env->DeleteLocalRef(element); + } + return result; +} + +/// end helpers + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/**********************************/ +/********** WalletManager *********/ +/**********************************/ +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_createWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jstring language, + jboolean isTestNet) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + const char *_password = env->GetStringUTFChars(password, JNI_FALSE); + const char *_language = env->GetStringUTFChars(language, JNI_FALSE); + + Bitmonero::Wallet *wallet = + Bitmonero::WalletManagerFactory::getWalletManager()->createWallet( + std::string(_path), + std::string(_password), + std::string(_language), + isTestNet); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + env->ReleaseStringUTFChars(language, _language); + return reinterpret_cast(wallet); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jboolean isTestNet) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + const char *_password = env->GetStringUTFChars(password, JNI_FALSE); + + Bitmonero::Wallet *wallet = + Bitmonero::WalletManagerFactory::getWalletManager()->openWallet( + std::string(_path), + std::string(_password), + isTestNet); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + return reinterpret_cast(wallet); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring mnemonic, + jboolean isTestNet, + jlong restoreHeight) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + const char *_mnemonic = env->GetStringUTFChars(mnemonic, JNI_FALSE); + + Bitmonero::Wallet *wallet = + Bitmonero::WalletManagerFactory::getWalletManager()->recoveryWallet( + std::string(_path), + std::string(_mnemonic), + isTestNet, + restoreHeight); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(mnemonic, _mnemonic); + return reinterpret_cast(wallet); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance, + jstring path, jstring language, + jboolean isTestNet, + jlong restoreHeight, + jstring addressString, + jstring viewKeyString, + jstring spendKeyString) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + const char *_language = env->GetStringUTFChars(language, JNI_FALSE); + const char *_addressString = env->GetStringUTFChars(addressString, JNI_FALSE); + const char *_viewKeyString = env->GetStringUTFChars(viewKeyString, JNI_FALSE); + const char *_spendKeyString = env->GetStringUTFChars(spendKeyString, JNI_FALSE); + + Bitmonero::Wallet *wallet = + Bitmonero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( + std::string(_path), + std::string(_language), + isTestNet, + restoreHeight, + std::string(_addressString), + std::string(_viewKeyString), + std::string(_spendKeyString)); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(language, _language); + env->ReleaseStringUTFChars(addressString, _addressString); + env->ReleaseStringUTFChars(viewKeyString, _viewKeyString); + env->ReleaseStringUTFChars(spendKeyString, _spendKeyString); + return reinterpret_cast(wallet); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + bool exists = + Bitmonero::WalletManagerFactory::getWalletManager()->walletExists(std::string(_path)); + env->ReleaseStringUTFChars(path, _path); + return exists; +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env, jobject instance, + jstring keys_file_name, + jstring password, + jboolean watch_only) { + const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, JNI_FALSE); + const char *_password = env->GetStringUTFChars(password, JNI_FALSE); + bool passwordOk = + Bitmonero::WalletManagerFactory::getWalletManager()->verifyWalletPassword( + std::string(_keys_file_name), std::string(_password), watch_only); + env->ReleaseStringUTFChars(keys_file_name, _keys_file_name); + env->ReleaseStringUTFChars(password, _password); + return passwordOk; +} + + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + std::vector walletPaths = + Bitmonero::WalletManagerFactory::getWalletManager()->findWallets(std::string(_path)); + env->ReleaseStringUTFChars(path, _path); + return cpp2java(env, walletPaths); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getErrorString(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->errorString().c_str()); +} + +//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; + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_setDaemonAddressJ(JNIEnv *env, jobject instance, + jstring address) { + const char *_address = env->GetStringUTFChars(address, JNI_FALSE); + Bitmonero::WalletManagerFactory::getWalletManager()->setDaemonAddress(std::string(_address)); + env->ReleaseStringUTFChars(address, _address); +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getConnectedDaemonVersion(JNIEnv *env, + jobject instance) { + uint32_t version; + bool isConnected = + Bitmonero::WalletManagerFactory::getWalletManager()->connected(&version); + if (!isConnected) version = 0; + return version; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockchainHeight(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->blockchainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockchainTargetHeight(JNIEnv *env, + jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->blockchainTargetHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getNetworkDifficulty(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->networkDifficulty(); +} + +JNIEXPORT jdouble JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getMiningHashRate(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->miningHashRate(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockTarget(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->blockTarget(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_isMining(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->isMining(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_startMining(JNIEnv *env, jobject instance, + jstring address, + jboolean background_mining, + jboolean ignore_battery) { + const char *_address = env->GetStringUTFChars(address, JNI_FALSE); + bool success = + Bitmonero::WalletManagerFactory::getWalletManager()->startMining(std::string(_address), + background_mining, + ignore_battery); + env->ReleaseStringUTFChars(address, _address); + return success; +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_stopMining(JNIEnv *env, jobject instance) { + return Bitmonero::WalletManagerFactory::getWalletManager()->stopMining(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobject instance, + jstring address, + jboolean dnssec_valid) { + const char *_address = env->GetStringUTFChars(address, JNI_FALSE); + bool _dnssec_valid = (bool) dnssec_valid; + std::string resolvedAlias = + Bitmonero::WalletManagerFactory::getWalletManager()->resolveOpenAlias( + std::string(_address), + _dnssec_valid); + env->ReleaseStringUTFChars(address, _address); + return env->NewStringUTF(resolvedAlias.c_str()); +} + +//TODO static std::tuple checkUpdates(const std::string &software, const std::string &subdir); + +// actually a WalletManager function, but logically in Wallet +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance, + jobject walletInstance) { + Bitmonero::Wallet *wallet = getHandle(env, walletInstance); + bool closeSuccess = Bitmonero::WalletManagerFactory::getWalletManager()->closeWallet(wallet); + if (closeSuccess) { + MyWalletListener *walletListener = getHandle(env, walletInstance, + "listenerHandle"); + if (walletListener != nullptr) { + walletListener->deleteGlobalJavaRef(env); + } + delete walletListener; + } + LOGD("wallet closed"); + return closeSuccess; +} + + + + +/**********************************/ +/************ Wallet **************/ +/**********************************/ + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSeed(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + const char *address = wallet->seed().c_str(); + return env->NewStringUTF(address); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSeedLanguage(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + const char *address = wallet->getSeedLanguage().c_str(); + return env->NewStringUTF(address); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setSeedLanguage(JNIEnv *env, jobject instance, + jstring language) { + const char *_language = env->GetStringUTFChars(language, JNI_FALSE); + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->setSeedLanguage(std::string(_language)); + env->ReleaseStringUTFChars(language, _language); +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getStatusJ(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->status(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getErrorString(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->errorString().c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setPassword(JNIEnv *env, jobject instance, + jstring password) { + const char *_password = env->GetStringUTFChars(password, JNI_FALSE); + Bitmonero::Wallet *wallet = getHandle(env, instance); + bool success = wallet->setPassword(std::string(_password)); + env->ReleaseStringUTFChars(password, _password); + return success; +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAddress(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + const char *address = wallet->address().c_str(); + return env->NewStringUTF(address); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getPath(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + const char *path = wallet->path().c_str(); + return env->NewStringUTF(path); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isTestNet(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->testnet(); +} + +//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; +//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getIntegratedAddress(JNIEnv *env, jobject instance, + jstring payment_id) { + const char *_payment_id = env->GetStringUTFChars(payment_id, JNI_FALSE); + Bitmonero::Wallet *wallet = getHandle(env, instance); + std::string address = wallet->integratedAddress(_payment_id); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return env->NewStringUTF(address.c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSecretViewKey(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + //return env->NewStringUTF(wallet->secretViewKey().c_str()); // changed in head + return env->NewStringUTF(wallet->privateViewKey().c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_store(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, JNI_FALSE); + Bitmonero::Wallet *wallet = getHandle(env, instance); + bool success = wallet->store(std::string(_path)); + env->ReleaseStringUTFChars(path, _path); + return success; +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getFilename(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->filename().c_str()); +} + +// virtual std::string keysFilename() const = 0; + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_initJ(JNIEnv *env, jobject instance, + jstring daemon_address, + long upper_transaction_size_limit) { +// const std::string &daemon_username = "", const std::string &daemon_password = "") = 0; + const char *_daemon_address = env->GetStringUTFChars(daemon_address, JNI_FALSE); + Bitmonero::Wallet *wallet = getHandle(env, instance); + bool status = wallet->init(_daemon_address, upper_transaction_size_limit); + env->ReleaseStringUTFChars(daemon_address, _daemon_address); + return status; +} + +// 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; +// virtual bool connectToDaemon() = 0; + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getConnectionStatusJ(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->connected(); +} +//TODO virtual void setTrustedDaemon(bool arg) = 0; +//TODO virtual bool trustedDaemon() const = 0; + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getBalance(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->balance(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getUnlockedBalance(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->unlockedBalance(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isWatchOnly(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->watchOnly(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getBlockChainHeight(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->blockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getApproximateBlockChainHeight(JNIEnv *env, + jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->approximateBlockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDaemonBlockChainHeight(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->daemonBlockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDaemonBlockChainTargetHeight(JNIEnv *env, + jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->daemonBlockChainTargetHeight(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->synchronized(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDisplayAmount(JNIEnv *env, jobject clazz, + jlong amount) { + return env->NewStringUTF(Bitmonero::Wallet::displayAmount(amount).c_str()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAmountFromString(JNIEnv *env, jobject clazz, + jstring amount) { + const char *_amount = env->GetStringUTFChars(amount, JNI_FALSE); + uint64_t x = Bitmonero::Wallet::amountFromString(_amount); + env->ReleaseStringUTFChars(amount, _amount); + return x; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAmountFromDouble(JNIEnv *env, jobject clazz, + jdouble amount) { + return Bitmonero::Wallet::amountFromDouble(amount); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_generatePaymentId(JNIEnv *env, jobject clazz) { + return env->NewStringUTF(Bitmonero::Wallet::genPaymentId().c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isPaymentIdValid(JNIEnv *env, jobject clazz, + jstring payment_id) { + const char *_payment_id = env->GetStringUTFChars(payment_id, JNI_FALSE); + bool isValid = Bitmonero::Wallet::paymentIdValid(_payment_id); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return isValid; +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jobject clazz, + jstring address, jboolean isTestNet) { + const char *_address = env->GetStringUTFChars(address, JNI_FALSE); + bool isValid = Bitmonero::Wallet::addressValid(_address, isTestNet); + env->ReleaseStringUTFChars(address, _address); + return isValid; +} + +//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getPaymentIdFromAddress(JNIEnv *env, jobject clazz, + jstring address, + jboolean isTestNet) { + const char *_address = env->GetStringUTFChars(address, JNI_FALSE); + std::string payment_id = Bitmonero::Wallet::paymentIdFromAddress(_address, isTestNet); + env->ReleaseStringUTFChars(address, _address); + return env->NewStringUTF(payment_id.c_str()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getMaximumAllowedAmount(JNIEnv *env, jobject clazz) { + return Bitmonero::Wallet::maximumAllowedAmount(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_startRefresh(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->startRefresh(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_pauseRefresh(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->pauseRefresh(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_refresh(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->refresh(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_refreshAsync(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->refreshAsync(); +} + +//TODO virtual void setAutoRefreshInterval(int millis) = 0; +//TODO virtual int autoRefreshInterval() const = 0; + + +//virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, +// optional tvAmount, uint32_t mixin_count, +// PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + +//virtual PendingTransaction * createSweepUnmixableTransaction() = 0; + +//virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +//virtual bool submitTransaction(const std::string &fileName) = 0; +//virtual void disposeTransaction(PendingTransaction * t) = 0; +//virtual bool exportKeyImages(const std::string &filename) = 0; +//virtual bool importKeyImages(const std::string &filename) = 0; + + +//virtual TransactionHistory * history() const = 0; +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getHistoryJ(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return reinterpret_cast(wallet->history()); +} + +//virtual AddressBook * addressBook() const = 0; + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setListenerJ(JNIEnv *env, jobject instance, + jobject javaListener) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->setListener(nullptr); // clear old listener + // delete old listener + MyWalletListener *oldListener = getHandle(env, instance, + "listenerHandle"); + if (oldListener != nullptr) { + oldListener->deleteGlobalJavaRef(env); + delete oldListener; + } + if (javaListener == nullptr) { + LOGD("null listener"); + return 0; + } else { + MyWalletListener *listener = new MyWalletListener(env, javaListener); + wallet->setListener(listener); + return reinterpret_cast(listener); + } +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDefaultMixin(JNIEnv *env, jobject instance) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->defaultMixin(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setDefaultMixin(JNIEnv *env, jobject instance, jint mixin) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return wallet->setDefaultMixin(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; + +//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; + +//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; +//virtual bool rescanSpent() = 0; + + +// TransactionHistory +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_getCount(JNIEnv *env, jobject instance) { + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + return history->count(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_getTransactionByIndexJ(JNIEnv *env, + jobject instance, + jint i) { + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + Bitmonero::TransactionInfo *info = history->transaction(i); + return reinterpret_cast(info); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_getTransactionByIdJ(JNIEnv *env, + jobject instance, + jstring id) { + const char *_id = env->GetStringUTFChars(id, JNI_FALSE); + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + Bitmonero::TransactionInfo *info = history->transaction(std::string(_id)); + env->ReleaseStringUTFChars(id, _id); + return reinterpret_cast(info); +} + +jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { + jmethodID c = env->GetMethodID(class_TransactionInfo, "", "(J)V"); + return env->NewObject(class_TransactionInfo, c, reinterpret_cast(info)); +} + +#include +#include +jobject cpp2java(JNIEnv *env, std::vector vector) { + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + + //LOGD(std::to_string(vector.size()).c_str()); + jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, vector.size()); + for (Bitmonero::TransactionInfo *s: vector) { + jobject info = newTransactionInfo(env, s); + env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info); + env->DeleteLocalRef(info); + } + return arrayList; +} + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_getAll(JNIEnv *env, jobject instance) { + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + return cpp2java(env, history->getAll()); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_refresh(JNIEnv *env, jobject instance) { + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + history->refresh(); +} + +/* this is wrong - history object belongs to wallet +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_dispose(JNIEnv *env, jobject instance) { + Bitmonero::TransactionHistory *history = getHandle(env, + instance); + if (history != nullptr) { + setHandle(env, instance, 0); + delete history; + } +} +*/ + +// TransactionInfo +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getDirectionJ(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->direction(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_isPending(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->isPending(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_isFailed(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->isFailed(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getAmount(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->amount(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getFee(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->fee(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getBlockHeight(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->blockHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getConfirmations(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->confirmations(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getHash(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return env->NewStringUTF(info->hash().c_str()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getTimestamp(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->timestamp(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getPaymentId(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return env->NewStringUTF(info->paymentId().c_str()); +} + +jobject newTransferInstance(JNIEnv *env, jobject transactionInfo, long amount, + const std::string &address) { + + jmethodID methodID = env->GetMethodID(class_TransactionInfo$Transfer, "", + "(JL/java.lang/String;)V"); + jstring _address = env->NewStringUTF(address.c_str()); + jobject transfer = env->NewObject(class_TransactionInfo$Transfer, methodID, amount, _address); + env->DeleteLocalRef(_address); + return transfer; +} + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransfersJ(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + const std::vector &transfers = info->transfers(); + // make new ArrayList + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + jobject result = env->NewObject(class_ArrayList, java_util_ArrayList_, transfers.size()); + // create Transfer objects and stick them in the List + for (const Bitmonero::TransactionInfo::Transfer &s: transfers) { + jobject element = newTransferInstance(env, instance, s.amount, s.address); + env->CallBooleanMethod(result, java_util_ArrayList_add, element); + env->DeleteLocalRef(element); + } + return result; +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransferCount(JNIEnv *env, jobject instance) { + Bitmonero::TransactionInfo *info = getHandle(env, instance); + return info->transfers().size(); +} + + +#ifdef __cplusplus +} +#endif + diff --git a/app/src/main/cpp/monerujo.h b/app/src/main/cpp/monerujo.h new file mode 100644 index 0000000..3315e02 --- /dev/null +++ b/app/src/main/cpp/monerujo.h @@ -0,0 +1,51 @@ +/** + * 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. + */ + +#ifndef XMRWALLET_WALLET_LIB_H +#define XMRWALLET_WALLET_LIB_H + +#include +/* +#include + +#define LOG_TAG "[NDK]" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) +*/ + +jfieldID getHandleField(JNIEnv *env, jobject obj, const char* fieldName = "handle") { + jclass c = env->GetObjectClass(obj); + return env->GetFieldID(c, fieldName, "J"); // of type long +} + +template +T *getHandle(JNIEnv *env, jobject obj, const char* fieldName = "handle") { + jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName)); + return reinterpret_cast(handle); +} + +void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) { + env->SetLongField(obj, getHandleField(env, obj), handle); +} + +template +void setHandle(JNIEnv *env, jobject obj, T *t) { + jlong handle = reinterpret_cast(t); + setHandleFromLong(env, obj, handle); +} + +#endif //XMRWALLET_WALLET_LIB_H diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java new file mode 100644 index 0000000..e9149c4 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -0,0 +1,353 @@ +/** + * 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.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; + +import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.util.Helper; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class LoginActivity extends Activity { + static final String TAG = "LoginActivity"; + ListView listView; + List walletList = new ArrayList<>(); + List displayedList = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + + final EditText etDaemonAddress = (EditText) findViewById(R.id.etDaemonAddress); + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + + etDaemonAddress.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(etDaemonAddress, InputMethodManager.SHOW_IMPLICIT); + } + }); + etDaemonAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + return false; + } + return false; + } + }); + + ToggleButton tbMainNet = (ToggleButton) findViewById(R.id.tbMainNet); + tbMainNet.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean mainnet = ((ToggleButton) v).isChecked(); // current state + savePrefs(true); // use previous state as we just clicked it + if (mainnet) { + setDaemon(daemonMainNet); + } else { + setDaemon(daemonTestNet); + } + filterList(); + ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged(); + } + }); + + loadPrefs(); + + filterList(); + + listView = (ListView) findViewById(R.id.list); + ArrayAdapter adapter = new ArrayAdapter<>(this, + android.R.layout.simple_list_item_1, android.R.id.text1, this.displayedList); + + listView.setAdapter(adapter); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + EditText tvDaemonAddress = (EditText) findViewById(R.id.etDaemonAddress); + if (tvDaemonAddress.getText().toString().length() == 0) { + Toast.makeText(LoginActivity.this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_LONG).show(); + return; + } + + String itemValue = (String) listView.getItemAtPosition(position); + if ((isMainNet() && itemValue.charAt(1) != '4') + || (!isMainNet() && itemValue.charAt(1) != '9')) { + Toast.makeText(LoginActivity.this, getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); + return; + } + + final int preambleLength = "[123456] ".length(); + if (itemValue.length() <= (preambleLength)) { + Toast.makeText(LoginActivity.this, "something's wrong", Toast.LENGTH_LONG).show(); + return; + } + + String wallet = itemValue.substring(preambleLength); + promptPassword(wallet); + } + }); + if (Helper.getWritePermission(this)) { + new LoadListTask().execute(); + } else { + Log.i(TAG, "Waiting for permissions"); + } + } + + // adapted from http://www.mkyong.com/android/android-prompt-user-input-dialog-example/ + void promptPassword(final String wallet) { + Context context = LoginActivity.this; + LayoutInflater li = LayoutInflater.from(context); + View promptsView = li.inflate(R.layout.prompt_password, null); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setView(promptsView); + + final EditText etPassword = (EditText) promptsView.findViewById(R.id.etPassword); + final TextView tvPasswordLabel = (TextView) promptsView.findViewById(R.id.tvPasswordLabel); + + tvPasswordLabel.setText(LoginActivity.this.getString(R.string.prompt_password) + " " + wallet); + + // set dialog message + alertDialogBuilder + .setCancelable(false) + .setPositiveButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + String pass = etPassword.getText().toString(); + processPasswordEntry(wallet, pass); + } + }) + .setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + dialog.cancel(); + } + }); + + final AlertDialog alertDialog = alertDialogBuilder.create(); + // request keyboard + alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + // accept keyboard "ok" + etPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + String pass = etPassword.getText().toString(); + alertDialog.cancel(); + processPasswordEntry(wallet, pass); + return false; + } + return false; + } + }); + + alertDialog.show(); + } + + private void processPasswordEntry(String walletName, String pass) { + if (checkWalletPassword(walletName, pass)) { + startWallet(walletName, pass); + } else { + Toast.makeText(this, getString(R.string.bad_password), Toast.LENGTH_SHORT).show(); + } + } + + private boolean checkWalletPassword(String walletName, String password) { + String walletPath = new File(Helper.getStorageRoot(getApplicationContext()), + walletName + ".keys").getAbsolutePath(); + // only test view key + return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); + } + + + @Override + protected void onPause() { + Log.d(TAG, "onPause()"); + savePrefs(false); + super.onPause(); + } + + boolean isMainNet() { + ToggleButton tbMainNet = (ToggleButton) findViewById(R.id.tbMainNet); + return tbMainNet.isChecked(); + } + + void setMainNet(boolean mainnet) { + ToggleButton tbMainNet = (ToggleButton) findViewById(R.id.tbMainNet); + tbMainNet.setChecked(mainnet); + } + + String getDaemon() { + EditText tvDaemonAddress = (EditText) findViewById(R.id.etDaemonAddress); + return tvDaemonAddress.getText().toString(); + } + + void setDaemon(String address) { + EditText tvDaemonAddress = (EditText) findViewById(R.id.etDaemonAddress); + tvDaemonAddress.setText(address); + } + + private static final String PREF_DAEMON_TESTNET = "daemon_testnet"; + private static final String PREF_DAEMON_MAINNET = "daemon_mainnet"; + private static final String PREF_MAINNET = "mainnet"; + + private String daemonTestNet; + private String daemonMainNet; + + void loadPrefs() { + SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE); + + boolean mainnet = sharedPref.getBoolean(PREF_MAINNET, false); + daemonMainNet = sharedPref.getString(PREF_DAEMON_MAINNET, "localhost:18081"); + daemonTestNet = sharedPref.getString(PREF_DAEMON_TESTNET, "localhost:28081"); + + setMainNet(mainnet); + if (mainnet) { + setDaemon(daemonMainNet); + } else { + setDaemon(daemonTestNet); + } + } + + void savePrefs(boolean usePreviousState) { + // save the daemon address for the net + boolean mainnet = isMainNet() ^ usePreviousState; + String daemon = getDaemon(); + if (mainnet) { + daemonMainNet = daemon; + } else { + daemonTestNet = daemon; + } + + SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putBoolean(PREF_MAINNET, mainnet); + editor.putString(PREF_DAEMON_MAINNET, daemonMainNet); + editor.putString(PREF_DAEMON_TESTNET, daemonTestNet); + editor.apply(); + } + + void startWallet(String walletName, String walletPassword) { + Log.d(TAG, "startWallet()"); + savePrefs(false); + boolean testnet = !isMainNet(); + String daemon = getDaemon(); + + Intent intent = new Intent(getApplicationContext(), WalletActivity.class); + if (!daemon.contains(":")) { + daemon = daemon + (testnet ? ":28081" : ":18081"); + } + + WalletManager.getInstance().setDaemon(daemon, testnet); + + intent.putExtra(WalletActivity.REQUEST_ID, walletName); + intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); + startActivity(intent); + } + + private void filterList() { + displayedList.clear(); + char x = isMainNet() ? '4' : '9'; + for (String s : walletList) { + if (s.charAt(1) == x) displayedList.add(s); + } + } + + private class LoadListTask extends AsyncTask { + protected void onPreExecute() { + //Toast.makeText(LoginActivity.this, getString(R.string.status_walletlist_loading), Toast.LENGTH_LONG).show(); + } + + protected Integer doInBackground(String... params) { + WalletManager mgr = WalletManager.getInstance(); + List walletInfos = + mgr.findWallets(Helper.getStorageRoot(getApplicationContext())); + + walletList.clear(); + for (WalletManager.WalletInfo walletInfo : walletInfos) { + Log.d(TAG, walletInfo.address); + String displayAddress = walletInfo.address; + if (displayAddress.length() == 95) { + displayAddress = walletInfo.address.substring(0, 6); + } + walletList.add("[" + displayAddress + "] " + walletInfo.name); + } + return 0; + } + + protected void onPostExecute(Integer result) { + if (result == 0) { + filterList(); + ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged(); + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],@NonNull int[] grantResults) { + Log.d(TAG, "onRequestPermissionsResult()"); + switch (requestCode) { + case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + new LoadListTask().execute(); + } else { + String msg = getString(R.string.message_strorage_not_permitted); + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + break; + default: + } + } +} + diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java new file mode 100644 index 0000000..0c2349e --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -0,0 +1,351 @@ +/** + * 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.content.ClipData; +import android.content.ClipboardManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; +import com.m2049r.xmrwallet.model.TransactionInfo; +import com.m2049r.xmrwallet.model.Wallet; +import com.m2049r.xmrwallet.service.WalletService; + +import java.util.List; + +public class WalletActivity extends AppCompatActivity + implements TransactionInfoAdapter.OnInteractionListener, WalletService.Observer { + private static final String TAG = "WalletActivity"; + + public static final String REQUEST_ID = "id"; + public static final String REQUEST_PW = "pw"; + + TransactionInfoAdapter adapter; + + @Override + protected void onStart() { + super.onStart(); + Log.d(TAG, "onStart()"); + acquireWakeLock(); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + String walletId = extras.getString(REQUEST_ID); + String walletPassword = extras.getString(REQUEST_PW); + connectWalletService(walletId, walletPassword); + } else { + throw new IllegalStateException("No extras passed! Panic!"); + } + showProgress(); + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + onProgress(10); // look like we are working! + } + }, 250); + //Log.d(TAG, "onStart() done."); + } + + private String title = null; + + void setActivityTitle(Wallet wallet) { + if ((wallet == null) || (title != null)) return; + String shortName = wallet.getName(); + if (shortName.length() > 16) { + shortName = shortName.substring(0, 14) + "..."; + } + this.title = "[" + wallet.getAddress().substring(0, 6) + "] " + shortName; + setTitle(this.title); + onProgress(100); + Log.d(TAG, "wallet title is " + this.title); + } + + @Override + protected void onStop() { + Log.d(TAG, "onStop()"); + releaseWakeLock(); + disconnectWalletService(); + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy()"); + super.onDestroy(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + setContentView(R.layout.wallet_activity); + + // TODO do stuff with savedInstanceState + if (savedInstanceState != null) { + return; + } + //Log.d(TAG, "no savedInstanceState"); + + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list); + + RecyclerView.ItemDecoration itemDecoration = new + DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL); + recyclerView.addItemDecoration(itemDecoration); + + this.adapter = new TransactionInfoAdapter(this); + recyclerView.setAdapter(adapter); + + setTitle(getString(R.string.status_wallet_loading)); + //Log.d(TAG, "onCreate() done."); + } + + private long firstBlock = 0; + private boolean synced = false; + + private void updateStatus(Wallet wallet) { + setActivityTitle(wallet); + final TextView balanceView = (TextView) findViewById(R.id.tvBalance); + final TextView unlockedView = (TextView) findViewById(R.id.tvUnlockedBalance); + final TextView syncProgressView = (TextView) findViewById(R.id.tvBlockHeightProgress); + final TextView connectionStatusView = (TextView) findViewById(R.id.tvConnectionStatus); + + //Wallet wallet = getWallet(); + balanceView.setText(Wallet.getDisplayAmount(wallet.getBalance())); + unlockedView.setText(Wallet.getDisplayAmount(wallet.getUnlockedBalance())); + String sync = ""; + if (wallet.getConnectionStatus() == Wallet.ConnectionStatus.ConnectionStatus_Connected) { + if (!wallet.isSynchronized()) { + long n = wallet.getDaemonBlockChainHeight() - wallet.getBlockChainHeight(); + sync = n + " " + getString(R.string.status_remaining); + if (firstBlock == 0) { + firstBlock = wallet.getBlockChainHeight(); + } + int x = 100 - Math.round(100f * n / (1f * wallet.getDaemonBlockChainHeight() - firstBlock)); + //Log.d(TAG, n + "/" + (wallet.getDaemonBlockChainHeight() - firstBlock)); + onProgress(getString(R.string.status_syncing) + " " + sync); + onProgress(x); + } else { + sync = getString(R.string.status_synced) + ": " + wallet.getBlockChainHeight(); + if (!synced) { + hideProgress(); + synced = true; + } + } + } + String t = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet)); + syncProgressView.setText(sync); + connectionStatusView.setText(t + " " + wallet.getConnectionStatus().toString().substring(17)); + } + + @Override + public void onRefreshed(final Wallet wallet, final boolean full) { + Log.d(TAG, "onRefreshed()"); + if (wallet.isSynchronized()) { + releaseWakeLock(); // the idea is to stay awake until synced + } + runOnUiThread(new Runnable() { + public void run() { + if (full) { + List list = wallet.getHistory().getAll(); + adapter.setInfos(list); + adapter.notifyDataSetChanged(); + } + updateStatus(wallet); + } + }); + } + + Wallet getWallet() { + if (mBoundService == null) throw new IllegalStateException("WalletService not bound."); + return mBoundService.getWallet(); + } + + private WalletService mBoundService = null; + private boolean mIsBound = false; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + mBoundService = ((WalletService.WalletServiceBinder) service).getService(); + //Log.d(TAG, "setting observer of " + mBoundService); + mBoundService.setObserver(WalletActivity.this); + //TODO show current progress (eg. if the service is already busy saving last wallet) + Log.d(TAG, "CONNECTED"); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. + mBoundService = null; + setTitle(getString(R.string.wallet_activity_name)); + Log.d(TAG, "DISCONNECTED"); + } + }; + + void connectWalletService(String walletName, String walletPassword) { + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + Intent intent = new Intent(getApplicationContext(), WalletService.class); + intent.putExtra(WalletService.REQUEST_WALLET, walletName); + intent.putExtra(WalletService.REQUEST, WalletService.REQUEST_CMD_LOAD); + intent.putExtra(WalletService.REQUEST_CMD_LOAD_PW, walletPassword); + startService(intent); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + Log.d(TAG, "BOUND"); + } + + void disconnectWalletService() { + if (mIsBound) { + // Detach our existing connection. + mBoundService.setObserver(null); + unbindService(mConnection); + mIsBound = false; + Toast.makeText(getApplicationContext(), getString(R.string.status_wallet_unloading), Toast.LENGTH_LONG).show(); + Log.d(TAG, "UNBOUND"); + } + } + + // Callbacks from TransactionInfoAdapter + @Override + public void onInteraction(final View view, final TransactionInfo infoItem) { + final Context ctx = view.getContext(); + AlertDialog.Builder builder = new AlertDialog.Builder(ctx); + builder.setTitle("Transaction details"); + + builder.setNegativeButton("Copy TX ID", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ClipboardManager clipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("TX", infoItem.getHash()); + clipboardManager.setPrimaryClip(clip); + } + }); + + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + builder.setMessage("TX ID: " + infoItem.getHash() + + "\nPayment ID: " + infoItem.getPaymentId() + + "\nBlockHeight: " + infoItem.getBlockHeight() + + "\nAmount: " + Wallet.getDisplayAmount(infoItem.getAmount()) + + "\nFee: " + Wallet.getDisplayAmount(infoItem.getFee())); + AlertDialog alert1 = builder.create(); + alert1.show(); + } + + @Override + protected void onPause() { + Log.d(TAG, "onPause()"); + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + Log.d(TAG, "onResume()"); + } + + private PowerManager.WakeLock wl = null; + + void acquireWakeLock() { + if ((wl != null) && wl.isHeld()) return; + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + this.wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, getString(R.string.app_name)); + try { + wl.acquire(); + Log.d(TAG, "WakeLock acquired"); + } catch (SecurityException ex) { + Log.d(TAG, "WakeLock NOT acquired"); + Log.d(TAG, ex.getLocalizedMessage()); + wl = null; + } + } + + void releaseWakeLock() { + if ((wl == null) || !wl.isHeld()) return; + wl.release(); + wl = null; + Log.d(TAG, "WakeLock released"); + } + + private void showProgress() { + runOnUiThread(new Runnable() { + public void run() { + LinearLayout llProgress = (LinearLayout) findViewById(R.id.llProgress); + llProgress.setVisibility(View.VISIBLE); + } + }); + } + + private void hideProgress() { + runOnUiThread(new Runnable() { + public void run() { + LinearLayout llProgress = (LinearLayout) findViewById(R.id.llProgress); + llProgress.setVisibility(View.GONE); + } + }); + } + + @Override + public void onProgress(final String text) { + runOnUiThread(new Runnable() { + public void run() { + TextView progressText = (TextView) findViewById(R.id.tvProgress); + progressText.setText(text); + } + }); + } + + @Override + public void onProgress(final int n) { + runOnUiThread(new Runnable() { + public void run() { + ProgressBar progress = (ProgressBar) findViewById(R.id.pbProgress); + progress.setProgress(n); + } + }); + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java new file mode 100644 index 0000000..e2a730b --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java @@ -0,0 +1,165 @@ +/** + * 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; +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.TextView; + +import com.m2049r.xmrwallet.R; +import com.m2049r.xmrwallet.model.TransactionInfo; +import com.m2049r.xmrwallet.model.Wallet; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +public class TransactionInfoAdapter extends RecyclerView.Adapter { + 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"); + + static final int TX_RED = Color.rgb(255, 79, 65); + static final int TX_GREEN = Color.rgb(54, 176, 91); + + public interface OnInteractionListener { + void onInteraction(View view, TransactionInfo item); + } + + private final List infoItems; + private final OnInteractionListener listener; + + public TransactionInfoAdapter(OnInteractionListener listener) { + this.infoItems = new ArrayList<>(); + this.listener = listener; + Calendar cal = Calendar.getInstance(); + TimeZone tz = cal.getTimeZone(); //get the local time zone. + DATE_FORMATTER.setTimeZone(tz); + TIME_FORMATTER.setTimeZone(tz); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.transaction_item, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + holder.bind(position); + } + + @Override + public int getItemCount() { + return infoItems.size(); + } + + public void setInfos(List data) { + // TODO do stuff with data so we can really recycle elements (i.e. add only new tx) + this.infoItems.clear(); + if (data != null) { + Log.d(TAG, "setInfos " + data.size()); + // sort by block height + Collections.sort(data, new Comparator() { + @Override + public int compare(TransactionInfo o1, TransactionInfo o2) { + long b1 = o1.getBlockHeight(); + long b2 = o2.getBlockHeight(); + return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0; + } + }); + this.infoItems.addAll(data); + } else { + Log.d(TAG, "setInfos null"); + } + 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; + + public 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 + this.tvAmountPoint = (TextView) itemView.findViewById(R.id.tx_amount_point); + this.tvAmountDecimal = (TextView) itemView.findViewById(R.id.tx_amount_decimal); + this.tvDate = (TextView) itemView.findViewById(R.id.tx_date); + this.tvTime = (TextView) itemView.findViewById(R.id.tx_time); + } + + private String getDate(long time) { + return DATE_FORMATTER.format(new Date(time * 1000)); + } + + private String getTime(long time) { + return TIME_FORMATTER.format(new Date(time * 1000)); + } + + private void setTxColour(int clr) { + tvAmount.setTextColor(clr); + tvAmountDecimal.setTextColor(clr); + tvAmountPoint.setTextColor(clr); + } + + void bind(int position) { + this.infoItem = infoItems.get(position); + String displayAmount = Wallet.getDisplayAmount(infoItem.getAmount()); + // TODO fix this with i8n code + 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) { + setTxColour(TX_GREEN); + } else { + setTxColour(TX_RED); + } + this.tvDate.setText(getDate(infoItem.getTimestamp())); + this.tvTime.setText(getTime(infoItem.getTimestamp())); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + if (listener != null) { + int position = getAdapterPosition(); // gets item position + if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it + listener.onInteraction(view, infoItems.get(position)); + } + } + } + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java new file mode 100644 index 0000000..120f6bb --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java @@ -0,0 +1,62 @@ +/** + * 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.List; + +public class TransactionHistory { + static { + System.loadLibrary("monerujo"); + } + + private long handle; + + public TransactionHistory(long handle) { + 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 getAll() { + List handles = getAllJ(); + List infoList = new ArrayList(); + 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 getTransactionByIdJ(String id); + + public native List getAll(); + + public native void refresh(); + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java new file mode 100644 index 0000000..48634d7 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java @@ -0,0 +1,94 @@ +/** + * 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"); + } + + public long handle; + + public TransactionInfo(long handle) { + this.handle = handle; + } + + public enum Direction { + Direction_In, + Direction_Out + } + + public class Transfer { + long amount; + String address; + + public Transfer(long amount, String address) { + this.amount = amount; + this.address = address; + } + + public long getAmount() { + return amount; + } + + public String getAddress() { + return address; + } + } + + public String toString() { + return getDirection() + "@" + getBlockHeight() + " " + getAmount(); + } + + public Direction getDirection() { + return TransactionInfo.Direction.values()[getDirectionJ()]; + } + + 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 transfers; + + public List getTransfers() { // not threadsafe + if (this.transfers == null) { + this.transfers = getTransfersJ(); + } + return this.transfers; + } + + private native List getTransfersJ(); +*/ + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java new file mode 100644 index 0000000..2e2dfaa --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java @@ -0,0 +1,215 @@ +/** + * 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"); + } + + static final String TAG = "Wallet"; + + public String getName() { + String p = getPath(); + return new File(p).getName(); + } + + private long handle = 0; + private long listenerHandle = 0; + + public Wallet(long handle) { + this.handle = handle; + } + + public enum Status { + Status_Ok, + Status_Error, + Status_Critical + } + + public enum ConnectionStatus { + ConnectionStatus_Disconnected, + ConnectionStatus_Connected, + ConnectionStatus_WrongVersion + } + + //public native long createWalletListenerJ(); + + public native String getSeed(); + + public native String getSeedLanguage(); + + public native void setSeedLanguage(String language); + + public Status getStatus() { + return Wallet.Status.values()[getStatusJ()]; + } + + private native int getStatusJ(); + + public native String getErrorString(); + + public native boolean setPassword(String password); + + public native String getAddress(); + + public native String getPath(); + + public native boolean isTestNet(); + +//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; +//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; + + public native String getIntegratedAddress(String payment_id); + + public native String getSecretViewKey(); + + public boolean store() { + return store(this.getPath()); + } + + public native boolean store(String path); + + public boolean close() { + return WalletManager.getInstance().close(this); + } + + public native String getFilename(); + + // virtual std::string keysFilename() const = 0; + 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; +// virtual bool connectToDaemon() = 0; + + public ConnectionStatus getConnectionStatus() { + int s = getConnectionStatusJ(); + return Wallet.ConnectionStatus.values()[s]; + } + + private native int getConnectionStatusJ(); + +//TODO virtual void setTrustedDaemon(bool arg) = 0; +//TODO virtual bool trustedDaemon() const = 0; + + public native long getBalance(); + + public native long getUnlockedBalance(); + + public native boolean isWatchOnly(); + + public native long getBlockChainHeight(); + + public native long getApproximateBlockChainHeight(); + + public native long getDaemonBlockChainHeight(); + + public native long getDaemonBlockChainTargetHeight(); + + public native boolean isSynchronized(); + + public static native String getDisplayAmount(long amount); + + public static native long getAmountFromString(String amount); + + public static native long getAmountFromDouble(double amount); + + public static native String generatePaymentId(); + + public static native boolean isPaymentIdValid(String payment_id); + + public static native boolean isAddressValid(String address, boolean isTestNet); + +//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); + + public static native String getPaymentIdFromAddress(String address, boolean isTestNet); + + public static native long getMaximumAllowedAmount(); + + public native void startRefresh(); + + public native void pauseRefresh(); + + public native boolean refresh(); + + public native void refreshAsync(); + +//TODO virtual void setAutoRefreshInterval(int millis) = 0; +//TODO virtual int autoRefreshInterval() const = 0; + + +//virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, +// optional tvAmount, uint32_t mixin_count, +// PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + +//virtual PendingTransaction * createSweepUnmixableTransaction() = 0; + +//virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +//virtual bool submitTransaction(const std::string &fileName) = 0; +//virtual void disposeTransaction(PendingTransaction * t) = 0; +//virtual bool exportKeyImages(const std::string &filename) = 0; +//virtual bool importKeyImages(const std::string &filename) = 0; + + +//virtual TransactionHistory * history() const = 0; + + private TransactionHistory history = null; + + public TransactionHistory getHistory() { + if (history == null) { + history = new TransactionHistory(getHistoryJ()); + } + return history; + } + + private native long getHistoryJ(); + +//virtual AddressBook * addressBook() const = 0; +//virtual void setListener(WalletListener *) = 0; + + private native long setListenerJ(WalletListener listener); + + public void setListener(WalletListener listener) { + this.listenerHandle = setListenerJ(listener); + } + + public native int getDefaultMixin(); + + 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; + +//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; + +//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; +//virtual bool rescanSpent() = 0; + + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletListener.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletListener.java new file mode 100644 index 0000000..ea7f1f4 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletListener.java @@ -0,0 +1,57 @@ +/** + * 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 { + /** + * moneySpent - called when money spent + * @param txId - transaction id + * @param amount - tvAmount + */ + void moneySpent(String txId, long amount); + + /** + * moneyReceived - called when money received + * @param txId - transaction id + * @param amount - tvAmount + */ + void moneyReceived(String txId, long amount); + + /** + * unconfirmedMoneyReceived - called when payment arrived in tx pool + * @param txId - transaction id + * @param amount - tvAmount + */ + void unconfirmedMoneyReceived(String txId, long amount); + + /** + * newBlock - called when new block received + * @param height - block height + */ + void newBlock(long height); + + /** + * updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; + */ + void updated(); + + /** + * refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously + */ + void refreshed(); + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java new file mode 100644 index 0000000..1c9e46d --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -0,0 +1,230 @@ +/** + * 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.BufferedReader; +import java.io.File; +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"; + + static { + System.loadLibrary("monerujo"); + } + + // 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 + if (WalletManager.Instance == null) { + WalletManager.Instance = new WalletManager(); + } + return WalletManager.Instance; + } + + private WalletManager() { + this.managedWallets = new HashMap<>(); + } + + private Map managedWallets; + + public Wallet getWallet(String walletId) { + return managedWallets.get(walletId); + } + + private void manageWallet(String walletId, Wallet wallet) { + if (getWallet(walletId) != null) { + throw new IllegalStateException("Wallet already under management!"); + } + Log.d(TAG, "Managing " + walletId); + managedWallets.put(walletId, wallet); + } + + private void unmanageWallet(String walletId) { + if (getWallet(walletId) == null) { + throw new IllegalStateException("Wallet 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; + } + + public Wallet openWallet(String path, String password) { + long walletHandle = openWalletJ(path, password, isTestNet()); + Wallet wallet = new Wallet(walletHandle); + manageWallet(wallet.getName(), wallet); + return wallet; + } + + 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 createWalletJ(String path, String password, String language, boolean isTestNet); + + private native long openWalletJ(String path, String password, boolean isTestNet); + + private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight); + + private native long createWalletFromKeysJ(String path, String language, + boolean isTestNet, + long restoreHeight, + String addressString, + 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); + + //public native List findWallets(String path); // this does not work - some error in boost + + public class WalletInfo { + public File path; + public String name; + public String address; + } + + public List findWallets(File path) { + List wallets = new ArrayList<>(); + Log.d(TAG, "Scanning: " + path.getAbsolutePath()); + File[] found = path.listFiles(new FilenameFilter() { + public boolean accept(File dir, String filename) { + return filename.endsWith(".keys"); + } + }); + for (int i = 0; i < found.length; i++) { + WalletInfo info = new WalletInfo(); + info.path = path; + 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()); + info.address = "??????"; + BufferedReader addressReader = null; + try { + addressReader = new BufferedReader(new FileReader(addressFile)); + info.address = addressReader.readLine(); + } catch (IOException ex) { + Log.d(TAG, ex.getLocalizedMessage()); + } finally { + if (addressReader != null) { + try { + addressReader.close(); + } catch (IOException ex) { + // that's just too bad + } + } + } + wallets.add(info); + } + return wallets; + } + + public native String getErrorString(); + +//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0; + + private String daemonAddress = null; + private boolean testnet = true; + + 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 long getBlockchainHeight(); + + public native long getBlockchainTargetHeight(); + + public native long getNetworkDifficulty(); + + public native double getMiningHashRate(); + + public native long getBlockTarget(); + + public native boolean isMining(); + + public native boolean startMining(String address, boolean background_mining, boolean ignore_battery); + + public native boolean stopMining(); + + public native String resolveOpenAlias(String address, boolean dnssec_valid); + +//TODO static std::tuple checkUpdates(const std::string &software, const std::string &subdir); + + +} \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java new file mode 100644 index 0000000..5517b0e --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -0,0 +1,352 @@ +/** + * 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.app.ProgressDialog; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.util.Log; +import android.widget.Toast; + +import com.m2049r.xmrwallet.R; +import com.m2049r.xmrwallet.model.Wallet; +import com.m2049r.xmrwallet.model.WalletListener; +import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.util.Helper; + +// Bind / Unbind +// Activity onCreate() / onDestroy() +// or +// Activity onStart() / onStop() + + +public class WalletService extends Service { + final static String TAG = "WalletService"; + + public static final String REQUEST = "request"; + public static final String REQUEST_WALLET = "wallet"; + public static final String REQUEST_CMD_LOAD = "load"; + public static final String REQUEST_CMD_LOAD_PW = "walletPassword"; + + public static final int START_SERVICE = 1; + public static final int STOP_SERVICE = 2; + + private MyWalletListener listener = null; + + private class MyWalletListener implements WalletListener { + private Wallet wallet; + boolean updated = true; + + Wallet getWallet() { + return wallet; + } + + MyWalletListener(Wallet aWallet) { + if (aWallet == null) throw new IllegalArgumentException("Cannot open wallet!"); + this.wallet = aWallet; + } + + public void start() { + Log.d(TAG, "MyWalletListener.start()"); + if (wallet == null) throw new IllegalStateException("No wallet!"); + //acquireWakeLock(); + wallet.setListener(this); + wallet.startRefresh(); + } + + public void stop() { + Log.d(TAG, "MyWalletListener.stop()"); + if (wallet == null) throw new IllegalStateException("No wallet!"); + wallet.pauseRefresh(); + wallet.setListener(null); + //releaseWakeLock(); + } + + // WalletListener callbacks + public void moneySpent(String txId, long amount) { + Log.d(TAG, "moneySpent() " + amount + " @ " + txId); + } + + public void moneyReceived(String txId, long amount) { + Log.d(TAG, "moneyReceived() " + amount + " @ " + txId); + } + + public void unconfirmedMoneyReceived(String txId, long amount) { + Log.d(TAG, "unconfirmedMoneyReceived() " + amount + " @ " + txId); + } + + long lastBlockTime = 0; + + public void newBlock(long height) { + if (wallet == null) throw new IllegalStateException("No wallet!"); + // don't flood with an update for every block ... + if (lastBlockTime < System.currentTimeMillis() - 2000) { + Log.d(TAG, "newBlock() @" + height + "with observer " + observer); + lastBlockTime = System.currentTimeMillis(); + if (observer != null) { + observer.onRefreshed(wallet, false); + } + } + } + + public void updated() { + Log.d(TAG, "updated() " + wallet.getBalance()); + if (wallet == null) throw new IllegalStateException("No wallet!"); + updated = true; + } + + public void refreshed() { + if (wallet == null) throw new IllegalStateException("No wallet!"); + Log.d(TAG, "refreshed() " + wallet.getBalance() + " sync=" + wallet.isSynchronized() + "with observer " + observer); + if (updated) { + if (observer != null) { + wallet.getHistory().refresh(); + observer.onRefreshed(wallet, true); + updated = false; + } + } + } + } + + ///////////////////////////////////////////// + // communication back to client (activity) // + ///////////////////////////////////////////// + // NB: This allows for only one observer, i.e. only a single activity bound here + + private Observer observer = null; + + public void setObserver(Observer anObserver) { + observer = anObserver; + Log.d(TAG, "setObserver " + observer); + } + + public interface Observer { + void onRefreshed(Wallet wallet, boolean full); + + void onProgress(String text); + + void onProgress(int n); + } + + private void showProgress(String text) { + if (observer != null) { + observer.onProgress(text); + } + } + + private void showProgress(int n) { + if (observer != null) { + observer.onProgress(n); + } + } + + // + public Wallet getWallet() { + if (listener == null) throw new IllegalStateException("no listener"); + return listener.getWallet(); + } + + ///////////////////////////////////////////// + ///////////////////////////////////////////// + + private Looper mServiceLooper; + private WalletService.ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + Log.d(TAG, "Handling " + msg.arg2); + switch (msg.arg2) { + case START_SERVICE: { + Bundle extras = msg.getData(); + String walletId = extras.getString(REQUEST_WALLET, null); + String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null); + Log.d(TAG, "LOAD wallet " + walletId);// + ":" + walletPw); + if (walletId != null) { + start(walletId, walletPw); // TODO What if this fails? + } + } + break; + case STOP_SERVICE: + stop(); + break; + default: + Log.e(TAG, "UNKNOWN " + msg.arg2); + } + } + } + + @Override + public void onCreate() { + //mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + //showNotification(); + + // We are using a HandlerThread and a Looper to avoid loading and closing + // concurrency + HandlerThread thread = new HandlerThread("WalletService", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new WalletService.ServiceHandler(mServiceLooper); + + Log.d(TAG, "Service created"); + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy()"); + // Cancel the persistent notification. + //mNM.cancel(NOTIFICATION); + if (this.listener != null) { + Log.w(TAG, "onDestroy() with active listener"); + // no need to stop() here because the wallet closing should have been triggered + // through onUnbind() already + } + } + + public class WalletServiceBinder extends Binder { + public WalletService getService() { + return WalletService.this; + } + } + + private final IBinder mBinder = new WalletServiceBinder(); + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // when the activity satrts the service, it expects to start it for a new wallet + // the service is possibly still occupied with saving the last opened wallet + // so we queue the open request + // this should not matter since the old activity is not getting updates + // and the new one is not listening yet (although it will be bound) + Log.d(TAG, "onStartCommand()"); + //acquireWakeLock(); // we want to be awake for the fun stuff + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg2 = START_SERVICE; + msg.setData(intent.getExtras()); + mServiceHandler.sendMessage(msg); + //Log.d(TAG, "onStartCommand() message sent"); + return START_NOT_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // Very first client binds + Log.d(TAG, "onBind()"); + return mBinder; + } + + @Override + public boolean onUnbind(Intent intent) { + Log.d(TAG, "onUnbind()"); + // All clients have unbound with unbindService() + Message msg = mServiceHandler.obtainMessage(); + msg.arg2 = STOP_SERVICE; + mServiceHandler.sendMessage(msg); + Log.d(TAG, "onUnbind() message sent"); + return true; // true is important so that onUnbind is also called next time + } + + private void start(String walletName, String walletPassword) { + // if there is an listener it is always started / syncing + Log.d(TAG, "start()"); + showProgress(getString(R.string.status_wallet_loading)); + showProgress(10); + if (listener == null) { + Log.d(TAG, "start() loadWallet"); + Wallet aWallet = loadWallet(walletName, walletPassword); + listener = new MyWalletListener(aWallet); + listener.start(); + showProgress(95); + } + Log.d(TAG, "start() done"); + } + + public void stop() { + Log.d(TAG, "stop()"); + setObserver(null); // in case it was not reset already + if (listener != null) { + listener.stop(); + Log.d(TAG, "stop() closing"); + listener.getWallet().close(); + Log.d(TAG, "stop() closed"); + listener = null; + } + stopSelf(); + // TODO ensure the Looper & thread actually stop and go away? + } + + private Wallet loadWallet(String walletName, String walletPassword) { + String path = Helper.getWalletPath(getApplicationContext(), walletName); + //Log.d(TAG, "open wallet " + path); + Wallet wallet = openWallet(walletName, walletPassword); + //Log.d(TAG, "wallet opened: " + wallet); + if (wallet != null) { + //Log.d(TAG, wallet.getStatus().toString()); + Log.d(TAG, "Using daemon " + WalletManager.getInstance().getDaemonAddress()); + showProgress(55); + wallet.init(0); + showProgress(90); + Log.d(TAG, wallet.getConnectionStatus().toString()); + } + return wallet; + } + + private Wallet openWallet(String walletName, String walletPassword) { + String path = Helper.getWalletPath(getApplicationContext(), walletName); + showProgress(20); + Wallet wallet = null; + WalletManager walletMgr = WalletManager.getInstance(); + Log.d(TAG, "WalletManager testnet=" + walletMgr.isTestNet()); + showProgress(30); + if (walletMgr.walletExists(path)) { + Log.d(TAG, "open wallet " + path); + wallet = walletMgr.openWallet(path, walletPassword); + showProgress(60); + Log.d(TAG, "wallet opened"); + Wallet.Status status = wallet.getStatus(); + Log.d(TAG, "wallet status is " + status); + if (status != Wallet.Status.Status_Ok) { + Log.d(TAG, "wallet status is " + status); + WalletManager.getInstance().close(wallet); // TODO close() failed? + wallet = null; + // TODO what do we do with the progress?? + } + } + return wallet; + } +} + diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java new file mode 100644 index 0000000..118056d --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java @@ -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.util; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Environment; +import android.util.Log; + +import com.m2049r.xmrwallet.R; + +import java.io.File; + +public class Helper { + static final String TAG = "Helper"; + static final String WALLET_DIR = "Monerujo"; + + static public File getStorageRoot(Context context) { + if (!isExternalStorageWritable()) { + String msg = context.getString(R.string.message_strorage_not_writable); + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + File dir = new File(Environment.getExternalStorageDirectory(), WALLET_DIR); + if (!dir.exists()) { + Log.i(TAG, "Creating " + dir.getAbsolutePath()); + dir.mkdirs(); // try to make it + } + if (!dir.isDirectory()) { + String msg = "Directory " + dir.getAbsolutePath() + " does not exists."; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + return dir; + } + + public static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1; + + static public boolean getWritePermission(Activity context) { + 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"); + String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + return false; + } else { + return true; + } + } else { + return true; + } + } + + static public String getWalletPath(Context context, String aWalletName) { + File walletDir = getStorageRoot(context); + //d(TAG, "walletdir=" + walletDir.getAbsolutePath()); + File f = new File(walletDir, aWalletName); + Log.d(TAG, "wallet = " + f.getAbsolutePath() + " size=" + f.length()); + return f.getAbsolutePath(); + } + + /* 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; + } + +} diff --git a/app/src/main/jniLibs/armeabi-v7a/libblockchain_db.so b/app/src/main/jniLibs/armeabi-v7a/libblockchain_db.so new file mode 100644 index 0000000..f9dbb6b Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libblockchain_db.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcncrypto.so b/app/src/main/jniLibs/armeabi-v7a/libcncrypto.so new file mode 100644 index 0000000..a21be18 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcncrypto.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcommon.so b/app/src/main/jniLibs/armeabi-v7a/libcommon.so new file mode 100644 index 0000000..a742d86 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcommon.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcrypto.so b/app/src/main/jniLibs/armeabi-v7a/libcrypto.so new file mode 100644 index 0000000..b5d5c29 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcrypto.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcryptonote_basic.so b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_basic.so new file mode 100644 index 0000000..1d2b2f2 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_basic.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcryptonote_core.so b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_core.so new file mode 100644 index 0000000..3eb34ce Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_core.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libcryptonote_protocol.so b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_protocol.so new file mode 100644 index 0000000..c47d9fd Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libcryptonote_protocol.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libdaemonizer.so b/app/src/main/jniLibs/armeabi-v7a/libdaemonizer.so new file mode 100644 index 0000000..993a9c8 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libdaemonizer.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/liblmdb.so b/app/src/main/jniLibs/armeabi-v7a/liblmdb.so new file mode 100644 index 0000000..27df036 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/liblmdb.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libmnemonics.so b/app/src/main/jniLibs/armeabi-v7a/libmnemonics.so new file mode 100644 index 0000000..68f7373 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libmnemonics.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libp2p.so b/app/src/main/jniLibs/armeabi-v7a/libp2p.so new file mode 100644 index 0000000..7d5b0d6 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libp2p.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libringct.so b/app/src/main/jniLibs/armeabi-v7a/libringct.so new file mode 100644 index 0000000..a401500 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libringct.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/librpc.so b/app/src/main/jniLibs/armeabi-v7a/librpc.so new file mode 100644 index 0000000..eb55ca9 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/librpc.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libssl.so b/app/src/main/jniLibs/armeabi-v7a/libssl.so new file mode 100644 index 0000000..02c4695 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libssl.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libunbound.so b/app/src/main/jniLibs/armeabi-v7a/libunbound.so new file mode 100644 index 0000000..918c334 Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libunbound.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libwallet.so b/app/src/main/jniLibs/armeabi-v7a/libwallet.so new file mode 100644 index 0000000..a12975a Binary files /dev/null and b/app/src/main/jniLibs/armeabi-v7a/libwallet.so differ diff --git a/app/src/main/res/drawable/ic_info_black_24dp.xml b/app/src/main/res/drawable/ic_info_black_24dp.xml new file mode 100644 index 0000000..34b8202 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_navigate_next_black_24dp.xml b/app/src/main/res/drawable/ic_navigate_next_black_24dp.xml new file mode 100644 index 0000000..2483512 --- /dev/null +++ b/app/src/main/res/drawable/ic_navigate_next_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_notification_sync_32_32.png b/app/src/main/res/drawable/ic_notification_sync_32_32.png new file mode 100644 index 0000000..4b88148 Binary files /dev/null and b/app/src/main/res/drawable/ic_notification_sync_32_32.png differ diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml new file mode 100644 index 0000000..e3400cf --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_sync_black_24dp.xml b/app/src/main/res/drawable/ic_sync_black_24dp.xml new file mode 100644 index 0000000..5a283aa --- /dev/null +++ b/app/src/main/res/drawable/ic_sync_black_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/xmr_logo_256.png b/app/src/main/res/drawable/xmr_logo_256.png new file mode 100644 index 0000000..8abfb92 Binary files /dev/null and b/app/src/main/res/drawable/xmr_logo_256.png differ diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..5090c92 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/prompt_password.xml b/app/src/main/res/layout/prompt_password.xml new file mode 100644 index 0000000..738c5ea --- /dev/null +++ b/app/src/main/res/layout/prompt_password.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/transaction_item.xml b/app/src/main/res/layout/transaction_item.xml new file mode 100644 index 0000000..246d9e6 --- /dev/null +++ b/app/src/main/res/layout/transaction_item.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/wallet_activity.xml b/app/src/main/res/layout/wallet_activity.xml new file mode 100644 index 0000000..06beacd --- /dev/null +++ b/app/src/main/res/layout/wallet_activity.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..d5e3b8d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..fb8661d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..33119d5 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..6da0309 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b59f50f Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values/attrs_my_view.xml b/app/src/main/res/values/attrs_my_view.xml new file mode 100644 index 0000000..38a8727 --- /dev/null +++ b/app/src/main/res/values/attrs_my_view.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c6a0564 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + + #3F51B5 + #303F9F + #FF4081 + + #f0eeef + #1c1c1c + + + #36b05b + #ff4f41 + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..46c1ed2 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,6 @@ + + + 8dp + 8dp + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..708f234 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,41 @@ + + Monerujo + Select Wallet + Wallet + + + Daemon Address + Net Selection + TestNet + MainNet + Loading Wallet List + Loading Wallet … + Saving Wallet + Password for + Bad password! + Daemon address must be set! + Daemon does not fit wallet! + + Amount + Date + Balance + Available + Transactions + Daemon connected! + + Monerujo Service + Monerujo Service Running + Monerujo Service Stopped + Monerujo Service + Monerujo Service Connected + Monerujo Service Disconnected + + Synced + Blocks remaining + Syncing: + + Problems + External Storage is not writable! Panic! + External Storage permission not granted! Panic! + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..24e2703 --- /dev/null +++ b/build.gradle @@ -0,0 +1,22 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/doc/FAQ.md b/doc/FAQ.md new file mode 100644 index 0000000..627a14c --- /dev/null +++ b/doc/FAQ.md @@ -0,0 +1,46 @@ +# FAQ + +## Do you have any screenshots of what it looks like and how it works? + +### [Select Wallet](images/A-wallet_selection.png) + +Here you see a list of installed wallets and an entry field at the top to enter the daemon address. To the right there is a pushbutton to change between testnet and mainnet. The entered daemon is saved and displayed according to the state of this button. + +### [Wallet Password](images/B-enter_password.png) + +After selecting the wallet, the password is entered. + +### [Wallet Syncing](images/C-wallet_syncing.png) + +After some seconds the wallet is displyed with it's last known state and synced to the network. If it says "disconnected" or takes forever to show this screen then the entered daemon is wrong or unreachable. (Yes, I need to check the daemon availability on the login screen ...) Go back, and check that. + +During syncing, the number of remaining blocks is displayed - when this reaches 0 the blockchain is fully synced. + +The balance is updated during sync. + +### [Wallet Synced](images/D-wallet_synced.png) + +When the blockchain is synced, the screen shows "Synced" and the current blockchain height. When new blocks become available they are also synced and new transactions are displayed. + +## What features does it have? + +That's about it. Select a wallet and show the balance. Behind the scenes it keeps in sync with the blockchain while the app is open. So currently it is a view only wallet. You can use it to monitor your wallets on the go. + +In future it will have the possibility of executing transactions. And generating wallets. Technically, it can generate wallets now, but they are pointless since you need another client to make transactions anyway - so you can make the wallets on the other client. + +## What files do I need to copy? + +You need to copy the wallet files from you current Monero client. These are: +``` +WalletName +WalletName.address.txt +WalletName.keys +``` + +### From where? + +This depends on your installation - you could search for them in your home directory or check the settings of your current client. Places to try are `C:\Users\\Documents\Monero\wallets` for Windows or `~/.bitmonero/wallet` in Linux. Or just search for `WalletName.keys`. + +### What if don't have these files? + +As this is a view-only App right now, you need another client for generating wallets and sending transactions. This will change soonTM. diff --git a/doc/images/A-wallet_selection.png b/doc/images/A-wallet_selection.png new file mode 100644 index 0000000..26d31f7 Binary files /dev/null and b/doc/images/A-wallet_selection.png differ diff --git a/doc/images/B-enter_password.png b/doc/images/B-enter_password.png new file mode 100644 index 0000000..69d23be Binary files /dev/null and b/doc/images/B-enter_password.png differ diff --git a/doc/images/C-wallet_syncing.png b/doc/images/C-wallet_syncing.png new file mode 100644 index 0000000..7adefda Binary files /dev/null and b/doc/images/C-wallet_syncing.png differ diff --git a/doc/images/D-wallet_synced.png b/doc/images/D-wallet_synced.png new file mode 100644 index 0000000..9debc1e Binary files /dev/null and b/doc/images/D-wallet_synced.png differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_chrono.a b/external-libs/boost/lib/armeabi-v7a/libboost_chrono.a new file mode 100644 index 0000000..8acbe28 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_chrono.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_date_time.a b/external-libs/boost/lib/armeabi-v7a/libboost_date_time.a new file mode 100644 index 0000000..67f8456 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_date_time.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_filesystem.a b/external-libs/boost/lib/armeabi-v7a/libboost_filesystem.a new file mode 100644 index 0000000..4a62b94 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_filesystem.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_program_options.a b/external-libs/boost/lib/armeabi-v7a/libboost_program_options.a new file mode 100644 index 0000000..cbf5ea7 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_program_options.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_regex.a b/external-libs/boost/lib/armeabi-v7a/libboost_regex.a new file mode 100644 index 0000000..368c76b Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_regex.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_serialization.a b/external-libs/boost/lib/armeabi-v7a/libboost_serialization.a new file mode 100644 index 0000000..f103d87 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_serialization.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_system.a b/external-libs/boost/lib/armeabi-v7a/libboost_system.a new file mode 100644 index 0000000..c06ef2f Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_system.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_thread.a b/external-libs/boost/lib/armeabi-v7a/libboost_thread.a new file mode 100644 index 0000000..905151a Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_thread.a differ diff --git a/external-libs/boost/lib/armeabi-v7a/libboost_wserialization.a b/external-libs/boost/lib/armeabi-v7a/libboost_wserialization.a new file mode 100644 index 0000000..f5c3ee1 Binary files /dev/null and b/external-libs/boost/lib/armeabi-v7a/libboost_wserialization.a differ diff --git a/external-libs/monero/include/wallet2_api.h b/external-libs/monero/include/wallet2_api.h new file mode 100644 index 0000000..6a7419b --- /dev/null +++ b/external-libs/monero/include/wallet2_api.h @@ -0,0 +1,744 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + + +#include +#include +#include +#include + +// Public interface for libwallet library +namespace Monero { + + namespace Utils { + bool isAddressLocal(const std::string &hostaddr); + } + + template + class optional { + public: + optional(): set(false) {} + optional(const T &t): t(t), set(true) {} + const T &operator*() const { return t; } + T &operator*() { return t; } + operator bool() const { return set; } + private: + T t; + bool set; + }; + +/** + * @brief Transaction-like interface for sending money + */ +struct PendingTransaction +{ + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + enum Priority { + Priority_Low = 1, + Priority_Medium = 2, + Priority_High = 3, + Priority_Last + }; + + virtual ~PendingTransaction() = 0; + virtual int status() const = 0; + virtual std::string errorString() const = 0; + // commit transaction or save to file if filename is provided. + virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; + virtual std::vector txid() const = 0; + /*! + * \brief txCount - number of transactions current transaction will be splitted to + * \return + */ + virtual uint64_t txCount() const = 0; +}; + +/** + * @brief Transaction-like interface for sending money + */ +struct UnsignedTransaction +{ + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + enum Priority { + Priority_Low = 1, + Priority_Medium = 2, + Priority_High = 3, + Priority_Last + }; + + virtual ~UnsignedTransaction() = 0; + virtual int status() const = 0; + virtual std::string errorString() const = 0; + virtual std::vector amount() const = 0; + virtual std::vector fee() const = 0; + virtual std::vector mixin() const = 0; + // returns a string with information about all transactions. + virtual std::string confirmationMessage() const = 0; + virtual std::vector paymentId() const = 0; + virtual std::vector recipientAddress() const = 0; + virtual uint64_t minMixinCount() const = 0; + /*! + * \brief txCount - number of transactions current transaction will be splitted to + * \return + */ + virtual uint64_t txCount() const = 0; + /*! + * @brief sign - Sign txs and saves to file + * @param signedFileName + * return - true on success + */ + virtual bool sign(const std::string &signedFileName) = 0; +}; + +/** + * @brief The TransactionInfo - interface for displaying transaction information + */ +struct TransactionInfo +{ + enum Direction { + Direction_In, + Direction_Out + }; + + struct Transfer { + Transfer(uint64_t _amount, const std::string &address); + const uint64_t amount; + const std::string address; + }; + + virtual ~TransactionInfo() = 0; + virtual int direction() const = 0; + virtual bool isPending() const = 0; + virtual bool isFailed() const = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t fee() const = 0; + virtual uint64_t blockHeight() const = 0; + virtual uint64_t confirmations() const = 0; + //! transaction_id + virtual std::string hash() const = 0; + virtual std::time_t timestamp() const = 0; + virtual std::string paymentId() const = 0; + //! only applicable for output transactions + virtual const std::vector & transfers() const = 0; +}; +/** + * @brief The TransactionHistory - interface for displaying transaction history + */ +struct TransactionHistory +{ + virtual ~TransactionHistory() = 0; + virtual int count() const = 0; + virtual TransactionInfo * transaction(int index) const = 0; + virtual TransactionInfo * transaction(const std::string &id) const = 0; + virtual std::vector getAll() const = 0; + virtual void refresh() = 0; +}; + +/** + * @brief AddressBookRow - provides functions to manage address book + */ +struct AddressBookRow { +public: + AddressBookRow(std::size_t _rowId, const std::string &_address, const std::string &_paymentId, const std::string &_description): + m_rowId(_rowId), + m_address(_address), + m_paymentId(_paymentId), + m_description(_description) {} + +private: + std::size_t m_rowId; + std::string m_address; + std::string m_paymentId; + std::string m_description; +public: + std::string extra; + std::string getAddress() const {return m_address;} + std::string getDescription() const {return m_description;} + std::string getPaymentId() const {return m_paymentId;} + std::size_t getRowId() const {return m_rowId;} +}; + +/** + * @brief The AddressBook - interface for +Book + */ +struct AddressBook +{ + enum ErrorCode { + Status_Ok, + General_Error, + Invalid_Address, + Invalid_Payment_Id + }; + virtual ~AddressBook() = 0; + virtual std::vector getAll() const = 0; + virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; + virtual bool deleteRow(std::size_t rowId) = 0; + virtual void refresh() = 0; + virtual std::string errorString() const = 0; + virtual int errorCode() const = 0; + virtual int lookupPaymentID(const std::string &payment_id) const = 0; +}; + +struct WalletListener +{ + virtual ~WalletListener() = 0; + /** + * @brief moneySpent - called when money spent + * @param txId - transaction id + * @param amount - amount + */ + virtual void moneySpent(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief moneyReceived - called when money received + * @param txId - transaction id + * @param amount - amount + */ + virtual void moneyReceived(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief unconfirmedMoneyReceived - called when payment arrived in tx pool + * @param txId - transaction id + * @param amount - amount + */ + virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief newBlock - called when new block received + * @param height - block height + */ + virtual void newBlock(uint64_t height) = 0; + + /** + * @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; + */ + virtual void updated() = 0; + + + /** + * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously + */ + virtual void refreshed() = 0; +}; + + +/** + * @brief Interface for wallet operations. + * TODO: check if /include/IWallet.h is still actual + */ +struct Wallet +{ + + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + enum ConnectionStatus { + ConnectionStatus_Disconnected, + ConnectionStatus_Connected, + ConnectionStatus_WrongVersion + }; + + virtual ~Wallet() = 0; + virtual std::string seed() const = 0; + virtual std::string getSeedLanguage() const = 0; + virtual void setSeedLanguage(const std::string &arg) = 0; + //! returns wallet status (Status_Ok | Status_Error) + virtual int status() const = 0; + //! in case error status, returns error string + virtual std::string errorString() const = 0; + virtual bool setPassword(const std::string &password) = 0; + virtual std::string address() const = 0; + virtual std::string path() const = 0; + virtual bool testnet() const = 0; + //! returns current hard fork info + virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; + //! check if hard fork rules should be used + virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; + /*! + * \brief integratedAddress - returns integrated address for current wallet address and given payment_id. + * if passed "payment_id" param is an empty string or not-valid payment id string + * (16 characters hexadecimal string) - random payment_id will be generated + * + * \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be + * generated + * \return - 106 characters string representing integrated address + */ + virtual std::string integratedAddress(const std::string &payment_id) const = 0; + + /*! + * \brief privateViewKey - returns private view key + * \return - private view key + */ + virtual std::string privateViewKey() const = 0; + + /*! + * \brief store - stores wallet to file. + * \param path - main filename to store wallet to. additionally stores address file and keys file. + * to store to the same file - just pass empty string; + * \return + */ + virtual bool store(const std::string &path) = 0; + /*! + * \brief filename - returns wallet filename + * \return + */ + virtual std::string filename() const = 0; + /*! + * \brief keysFilename - returns keys filename. usually this formed as "wallet_filename".keys + * \return + */ + virtual std::string keysFilename() const = 0; + /*! + * \brief init - initializes wallet with daemon connection params. + * if daemon_address is local address, "trusted daemon" will be set to true forcibly + * startRefresh() should be called when wallet is initialized. + * + * \param daemon_address - daemon address in "hostname:port" format + * \param upper_transaction_size_limit + * \return - true on success + */ + virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username = "", const std::string &daemon_password = "") = 0; + + /*! + * \brief createWatchOnly - Creates a watch only wallet + * \param path - where to store the wallet + * \param password + * \param language + * \return - true if created successfully + */ + virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0; + + /*! + * \brief setRefreshFromBlockHeight - start refresh from block height on recover + * + * \param refresh_from_block_height - blockchain start height + */ + virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0; + + /*! + * \brief setRecoveringFromSeed - set state recover form seed + * + * \param recoveringFromSeed - true/false + */ + virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0; + + /** + * @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed + * @return + */ + virtual bool connectToDaemon() = 0; + + /** + * @brief connected - checks if the wallet connected to the daemon + * @return - true if connected + */ + virtual ConnectionStatus connected() const = 0; + virtual void setTrustedDaemon(bool arg) = 0; + virtual bool trustedDaemon() const = 0; + virtual uint64_t balance() const = 0; + virtual uint64_t unlockedBalance() const = 0; + + /** + * @brief watchOnly - checks if wallet is watch only + * @return - true if watch only + */ + virtual bool watchOnly() const = 0; + + /** + * @brief blockChainHeight - returns current blockchain height + * @return + */ + virtual uint64_t blockChainHeight() const = 0; + + /** + * @brief approximateBlockChainHeight - returns approximate blockchain height calculated from date/time + * @return + */ + virtual uint64_t approximateBlockChainHeight() const = 0; + + /** + * @brief daemonBlockChainHeight - returns daemon blockchain height + * @return 0 - in case error communicating with the daemon. + * status() will return Status_Error and errorString() will return verbose error description + */ + virtual uint64_t daemonBlockChainHeight() const = 0; + + /** + * @brief daemonBlockChainTargetHeight - returns daemon blockchain target height + * @return 0 - in case error communicating with the daemon. + * status() will return Status_Error and errorString() will return verbose error description + */ + virtual uint64_t daemonBlockChainTargetHeight() const = 0; + + /** + * @brief synchronized - checks if wallet was ever synchronized + * @return + */ + virtual bool synchronized() const = 0; + + static std::string displayAmount(uint64_t amount); + static uint64_t amountFromString(const std::string &amount); + static uint64_t amountFromDouble(double amount); + static std::string genPaymentId(); + static bool paymentIdValid(const std::string &paiment_id); + static bool addressValid(const std::string &str, bool testnet); + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); + static std::string paymentIdFromAddress(const std::string &str, bool testnet); + static uint64_t maximumAllowedAmount(); + // Easylogger wrapper + static void init(const char *argv0, const char *default_log_base_name); + static void debug(const std::string &str); + + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ + virtual void startRefresh() = 0; + /** + * @brief pauseRefresh - pause refresh thread + */ + virtual void pauseRefresh() = 0; + + /** + * @brief refresh - refreshes the wallet, updating transactions from daemon + * @return - true if refreshed successfully; + */ + virtual bool refresh() = 0; + + /** + * @brief refreshAsync - refreshes wallet asynchronously. + */ + virtual void refreshAsync() = 0; + + /** + * @brief setAutoRefreshInterval - setup interval for automatic refresh. + * @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled; + */ + virtual void setAutoRefreshInterval(int millis) = 0; + + /** + * @brief autoRefreshInterval - returns automatic refresh interval in millis + * @return + */ + virtual int autoRefreshInterval() const = 0; + + + /*! + * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored + * \param dst_addr destination address as string + * \param payment_id optional payment_id, can be empty string + * \param amount amount + * \param mixin_count mixin count. if 0 passed, wallet will use default value + * \param priority + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, + optional amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + + /*! + * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createSweepUnmixableTransaction() = 0; + + /*! + * \brief loadUnsignedTx - creates transaction from unsigned tx file + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; + + /*! + * \brief submitTransaction - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransaction(const std::string &fileName) = 0; + + + /*! + * \brief disposeTransaction - destroys transaction object + * \param t - pointer to the "PendingTransaction" object. Pointer is not valid after function returned; + */ + virtual void disposeTransaction(PendingTransaction * t) = 0; + + /*! + * \brief exportKeyImages - exports key images to file + * \param filename + * \return - true on success + */ + virtual bool exportKeyImages(const std::string &filename) = 0; + + /*! + * \brief importKeyImages - imports key images from file + * \param filename + * \return - true on success + */ + virtual bool importKeyImages(const std::string &filename) = 0; + + + virtual TransactionHistory * history() const = 0; + virtual AddressBook * addressBook() const = 0; + virtual void setListener(WalletListener *) = 0; + /*! + * \brief defaultMixin - returns number of mixins used in transactions + * \return + */ + virtual uint32_t defaultMixin() const = 0; + /*! + * \brief setDefaultMixin - setum number of mixins to be used for new transactions + * \param arg + */ + virtual void setDefaultMixin(uint32_t arg) = 0; + + /*! + * \brief setUserNote - attach an arbitrary string note to a txid + * \param txid - the transaction id to attach the note to + * \param note - the note + * \return true if succesful, false otherwise + */ + virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0; + /*! + * \brief getUserNote - return an arbitrary string note attached to a txid + * \param txid - the transaction id to attach the note to + * \return the attached note, or empty string if there is none + */ + virtual std::string getUserNote(const std::string &txid) const = 0; + virtual std::string getTxKey(const std::string &txid) const = 0; + + /* + * \brief signMessage - sign a message with the spend private key + * \param message - the message to sign (arbitrary byte data) + * \return the signature + */ + virtual std::string signMessage(const std::string &message) = 0; + /*! + * \brief verifySignedMessage - verify a signature matches a given message + * \param message - the message (arbitrary byte data) + * \param address - the address the signature claims to be made with + * \param signature - the signature + * \return true if the signature verified, false otherwise + */ + virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; + + virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; + /* + * \brief rescanSpent - Rescan spent outputs - Can only be used with trusted daemon + * \return true on success + */ + virtual bool rescanSpent() = 0; +}; + +/** + * @brief WalletManager - provides functions to manage wallets + */ +struct WalletManager +{ + + /*! + * \brief Creates new wallet + * \param path Name of wallet file + * \param password Password of wallet file + * \param language Language to be used to generate electrum seed memo + * \return Wallet instance (Wallet::status() needs to be called to check if created successfully) + */ + virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) = 0; + + /*! + * \brief Opens existing wallet + * \param path Name of wallet file + * \param password Password of wallet file + * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully) + */ + virtual Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) = 0; + + /*! + * \brief recovers existing wallet using memo (electrum seed) + * \param path Name of wallet file to be created + * \param memo memo (25 words electrum seed) + * \param testnet testnet + * \param restoreHeight restore from start height + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0; + + /*! + * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted + * \param path Name of wallet file to be created + * \param language language + * \param testnet testnet + * \param restoreHeight restore from start height + * \param addressString public address + * \param viewKeyString view key + * \param spendKeyString spend key (optional) + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createWalletFromKeys(const std::string &path, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") = 0; + + /*! + * \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted + * \param wallet previously opened / created wallet instance + * \return None + */ + virtual bool closeWallet(Wallet *wallet) = 0; + + /* + * ! checks if wallet with the given name already exists + */ + + /*! + * @brief TODO: delme walletExists - check if the given filename is the wallet + * @param path - filename + * @return + */ + virtual bool walletExists(const std::string &path) = 0; + + /*! + * @brief verifyWalletPassword - check if the given filename is the wallet + * @param keys_file_name - location of keys file + * @param password - password to verify + * @param watch_only - verify only view keys? + * @return + */ + virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, const bool watch_only) = 0; + + /*! + * \brief findWallets - searches for the wallet files by given path name recursively + * \param path - starting point to search + * \return - list of strings with found wallets (absolute paths); + */ + virtual std::vector findWallets(const std::string &path) = 0; + + /*! + * \brief checkPayment - checks a payment was made using a txkey + * \param address - the address the payment was sent to + * \param txid - the transaction id for that payment + * \param txkey - the transaction's secret key + * \param daemon_address - the address (host and port) to the daemon to request transaction data + * \param received - if succesful, will hold the amount of monero received + * \param height - if succesful, will hold the height of the transaction (0 if only in the pool) + * \param error - if unsuccesful, will hold an error string with more information about the error + * \return - true is succesful, false otherwise + */ + 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; + + //! returns verbose error string regarding last error; + virtual std::string errorString() const = 0; + + //! set the daemon address (hostname and port) + virtual void setDaemonAddress(const std::string &address) = 0; + + //! returns whether the daemon can be reached, and its version number + virtual bool connected(uint32_t *version = NULL) const = 0; + + //! returns current blockchain height + virtual uint64_t blockchainHeight() const = 0; + + //! returns current blockchain target height + virtual uint64_t blockchainTargetHeight() const = 0; + + //! returns current network difficulty + virtual uint64_t networkDifficulty() const = 0; + + //! returns current mining hash rate (0 if not mining) + virtual double miningHashRate() const = 0; + + //! returns current block target + virtual uint64_t blockTarget() const = 0; + + //! returns true iff mining + virtual bool isMining() const = 0; + + //! starts mining with the set number of threads + virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; + + //! stops mining + virtual bool stopMining() = 0; + + //! resolves an OpenAlias address to a monero address + virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; + + //! checks for an update and returns version, hash and url + static std::tuple checkUpdates(const std::string &software, const std::string &subdir); +}; + + +struct WalletManagerFactory +{ + // logging levels for underlying library + enum LogLevel { + LogLevel_Silent = -1, + LogLevel_0 = 0, + LogLevel_1 = 1, + LogLevel_2 = 2, + LogLevel_3 = 3, + LogLevel_4 = 4, + LogLevel_Min = LogLevel_Silent, + LogLevel_Max = LogLevel_4 + }; + + static WalletManager * getWalletManager(); + static void setLogLevel(int level); + static void setLogCategories(const std::string &categories); +}; + + +} + +namespace Bitmonero = Monero; + diff --git a/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a new file mode 100644 index 0000000..875ed7d Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libblockchain_db.so b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.so new file mode 100644 index 0000000..f9dbb6b Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libblocks.a b/external-libs/monero/lib/armeabi-v7a/libblocks.a new file mode 100644 index 0000000..62a86e4 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libblocks.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcncrypto.a b/external-libs/monero/lib/armeabi-v7a/libcncrypto.a new file mode 100644 index 0000000..bf59079 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcncrypto.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcncrypto.so b/external-libs/monero/lib/armeabi-v7a/libcncrypto.so new file mode 100644 index 0000000..a21be18 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcncrypto.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcommon.a b/external-libs/monero/lib/armeabi-v7a/libcommon.a new file mode 100644 index 0000000..009d6d3 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcommon.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcommon.so b/external-libs/monero/lib/armeabi-v7a/libcommon.so new file mode 100644 index 0000000..a742d86 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcommon.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a new file mode 100644 index 0000000..d578cc9 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.so b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.so new file mode 100644 index 0000000..1d2b2f2 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a new file mode 100644 index 0000000..e462875 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.so b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.so new file mode 100644 index 0000000..3eb34ce Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a new file mode 100644 index 0000000..496c699 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.so b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.so new file mode 100644 index 0000000..c47d9fd Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a new file mode 100644 index 0000000..06db80a Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libdaemonizer.so b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.so new file mode 100644 index 0000000..993a9c8 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libeasylogging.a b/external-libs/monero/lib/armeabi-v7a/libeasylogging.a new file mode 100644 index 0000000..028a12c Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libeasylogging.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libepee.a b/external-libs/monero/lib/armeabi-v7a/libepee.a new file mode 100644 index 0000000..c24969f Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libepee.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/liblmdb.a b/external-libs/monero/lib/armeabi-v7a/liblmdb.a new file mode 100644 index 0000000..18b69c3 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/liblmdb.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/liblmdb.so b/external-libs/monero/lib/armeabi-v7a/liblmdb.so new file mode 100644 index 0000000..27df036 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/liblmdb.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a b/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a new file mode 100644 index 0000000..341c329 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libmnemonics.a b/external-libs/monero/lib/armeabi-v7a/libmnemonics.a new file mode 100644 index 0000000..4589a99 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libmnemonics.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libmnemonics.so b/external-libs/monero/lib/armeabi-v7a/libmnemonics.so new file mode 100644 index 0000000..68f7373 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libmnemonics.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libp2p.a b/external-libs/monero/lib/armeabi-v7a/libp2p.a new file mode 100644 index 0000000..e1ea1c4 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libp2p.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libp2p.so b/external-libs/monero/lib/armeabi-v7a/libp2p.so new file mode 100644 index 0000000..7d5b0d6 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libp2p.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libringct.a b/external-libs/monero/lib/armeabi-v7a/libringct.a new file mode 100644 index 0000000..6bc6602 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libringct.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libringct.so b/external-libs/monero/lib/armeabi-v7a/libringct.so new file mode 100644 index 0000000..a401500 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libringct.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/librpc.a b/external-libs/monero/lib/armeabi-v7a/librpc.a new file mode 100644 index 0000000..71025e4 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/librpc.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/librpc.so b/external-libs/monero/lib/armeabi-v7a/librpc.so new file mode 100644 index 0000000..eb55ca9 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/librpc.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libunbound.a b/external-libs/monero/lib/armeabi-v7a/libunbound.a new file mode 100644 index 0000000..0a1f8c9 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libunbound.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libunbound.so b/external-libs/monero/lib/armeabi-v7a/libunbound.so new file mode 100644 index 0000000..918c334 Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libunbound.so differ diff --git a/external-libs/monero/lib/armeabi-v7a/libwallet.a b/external-libs/monero/lib/armeabi-v7a/libwallet.a new file mode 100644 index 0000000..8a1dcce Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libwallet.a differ diff --git a/external-libs/monero/lib/armeabi-v7a/libwallet.so b/external-libs/monero/lib/armeabi-v7a/libwallet.so new file mode 100644 index 0000000..a12975a Binary files /dev/null and b/external-libs/monero/lib/armeabi-v7a/libwallet.so differ diff --git a/external-libs/openssl/lib/armeabi-v7a/libcrypto.a b/external-libs/openssl/lib/armeabi-v7a/libcrypto.a new file mode 100644 index 0000000..86a949e Binary files /dev/null and b/external-libs/openssl/lib/armeabi-v7a/libcrypto.a differ diff --git a/external-libs/openssl/lib/armeabi-v7a/libcrypto.so b/external-libs/openssl/lib/armeabi-v7a/libcrypto.so new file mode 100644 index 0000000..b5d5c29 Binary files /dev/null and b/external-libs/openssl/lib/armeabi-v7a/libcrypto.so differ diff --git a/external-libs/openssl/lib/armeabi-v7a/libssl.a b/external-libs/openssl/lib/armeabi-v7a/libssl.a new file mode 100644 index 0000000..9607d61 Binary files /dev/null and b/external-libs/openssl/lib/armeabi-v7a/libssl.a differ diff --git a/external-libs/openssl/lib/armeabi-v7a/libssl.so b/external-libs/openssl/lib/armeabi-v7a/libssl.so new file mode 100644 index 0000000..02c4695 Binary files /dev/null and b/external-libs/openssl/lib/armeabi-v7a/libssl.so differ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8e2e979 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Apr 15 09:39:07 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..ee2784d --- /dev/null +++ b/local.properties @@ -0,0 +1,12 @@ +## This file is automatically generated by Android Studio. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Sat May 13 17:52:51 CEST 2017 +ndk.dir=C\:\\Users\\Test\\AppData\\Local\\Android\\Sdk\\ndk-bundle +sdk.dir=C\:\\Users\\Test\\AppData\\Local\\Android\\Sdk diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/xmrwallet.iml b/xmrwallet.iml new file mode 100644 index 0000000..4d7ac8d --- /dev/null +++ b/xmrwallet.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file