mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-02 15:53:04 +02:00
Compare commits
208 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
41a7b7ba61 | ||
![]() |
d739d8049e | ||
![]() |
91384b827a | ||
![]() |
386f6e744b | ||
![]() |
1f5ad86b07 | ||
![]() |
ef3a19b66e | ||
![]() |
9fd382e622 | ||
![]() |
f7c1cc9713 | ||
![]() |
30023e85bb | ||
![]() |
bf8780c874 | ||
![]() |
9379269f89 | ||
![]() |
a9b4abc01f | ||
![]() |
899ac775b1 | ||
![]() |
8b7c5a2450 | ||
![]() |
05c1ca5082 | ||
![]() |
6abf11841e | ||
![]() |
d46b8bf79a | ||
![]() |
149fd6376e | ||
![]() |
de70d64eb8 | ||
![]() |
232f7b801e | ||
![]() |
8962bd3050 | ||
![]() |
989d52b33d | ||
![]() |
a3c0ca7ebe | ||
![]() |
c9132d7d97 | ||
![]() |
758b042680 | ||
![]() |
84ce392192 | ||
![]() |
4ebcda2b14 | ||
![]() |
c49351a8a9 | ||
![]() |
41e84f2e29 | ||
![]() |
f08ddf93c8 | ||
![]() |
4bfc9c1bdd | ||
![]() |
1a13bdde91 | ||
![]() |
3e56d5a54b | ||
![]() |
c64fb1a769 | ||
![]() |
2d2a7d2db0 | ||
![]() |
ac9e24ee9f | ||
![]() |
059438a09e | ||
![]() |
d22fdfe2dc | ||
![]() |
48577e46aa | ||
![]() |
451371cd92 | ||
![]() |
cd6f646b63 | ||
![]() |
aa530caa28 | ||
![]() |
5df43f1274 | ||
![]() |
3b0cb3ffdb | ||
![]() |
300551dbe4 | ||
![]() |
942519adb7 | ||
![]() |
0652a8ba14 | ||
![]() |
04d0bd2ffb | ||
![]() |
8473e66c69 | ||
![]() |
2218ff615c | ||
![]() |
54b55b9f8f | ||
![]() |
5abf84f62b | ||
![]() |
6ff75d221f | ||
![]() |
c90f107c3c | ||
![]() |
a3db18032e | ||
![]() |
9db2c10679 | ||
![]() |
eab85c7f0a | ||
![]() |
6bf3229e77 | ||
![]() |
9f4f626acb | ||
![]() |
434dab55ba | ||
![]() |
59cc6b1864 | ||
![]() |
a6e9d0e77c | ||
![]() |
17df7c3faf | ||
![]() |
bf1829f775 | ||
![]() |
bc4aa0f772 | ||
![]() |
3f09e73df7 | ||
![]() |
11b7e23ad2 | ||
![]() |
ae48027689 | ||
![]() |
a42f750fc4 | ||
![]() |
3406f585f2 | ||
![]() |
7546637c89 | ||
![]() |
4da2106f04 | ||
![]() |
93c11fb90e | ||
![]() |
9fa710f75b | ||
![]() |
c53dd300bc | ||
![]() |
97f40648be | ||
![]() |
1ece6bfbeb | ||
![]() |
4b3b99ff2a | ||
![]() |
ca833d7017 | ||
![]() |
f1b6f859de | ||
![]() |
61d19c7066 | ||
![]() |
ffda0e965b | ||
![]() |
cc7cdb383c | ||
![]() |
ac1ea05ef6 | ||
![]() |
1ddd4f30b9 | ||
![]() |
1dc081834f | ||
![]() |
ce084927e1 | ||
![]() |
3610781f43 | ||
![]() |
ef3ddbac71 | ||
![]() |
0512af1496 | ||
![]() |
bd2c49669a | ||
![]() |
ac7831d0f9 | ||
![]() |
0f0b9a38c7 | ||
![]() |
807db19603 | ||
![]() |
c956f38899 | ||
![]() |
db68f517d3 | ||
![]() |
d4b293af80 | ||
![]() |
f7bbfc2fac | ||
![]() |
e08964749e | ||
![]() |
a05fa9d177 | ||
![]() |
7fe2fbe37d | ||
![]() |
40e30fed08 | ||
![]() |
320c7865ff | ||
![]() |
5e8cf8010e | ||
![]() |
e671fa19e0 | ||
![]() |
20d5b9a100 | ||
![]() |
5d489a634b | ||
![]() |
59b6f484fd | ||
![]() |
ecaa49d67d | ||
![]() |
d2dc53599e | ||
![]() |
4d8b26f97f | ||
![]() |
581c76e7be | ||
![]() |
6f66862870 | ||
![]() |
dd92f7bb36 | ||
![]() |
46808d306b | ||
![]() |
20503d2cbd | ||
![]() |
604691ca7e | ||
![]() |
1b626ba2b0 | ||
![]() |
0ed7bdfcee | ||
![]() |
524c3dd79f | ||
![]() |
197dffeae1 | ||
![]() |
cdb29bbc2e | ||
![]() |
7b96baeca7 | ||
![]() |
0712efec78 | ||
![]() |
341df6c6a3 | ||
![]() |
ab8fb82c1b | ||
![]() |
22d9173cea | ||
![]() |
05720e63ab | ||
![]() |
cdc2b23257 | ||
![]() |
9f0e89719c | ||
![]() |
190726e61c | ||
![]() |
729fafdb48 | ||
![]() |
b7ae23ac64 | ||
![]() |
27885f2c86 | ||
![]() |
ce9046c7b5 | ||
![]() |
506e6ce017 | ||
![]() |
f07b439731 | ||
![]() |
ac70ba8424 | ||
![]() |
84ec1ef418 | ||
![]() |
b576a9de3d | ||
![]() |
148faa00e4 | ||
![]() |
e82b471c14 | ||
![]() |
9ed92e5117 | ||
![]() |
303b3aa354 | ||
![]() |
d801a50962 | ||
![]() |
9cd8a75dc6 | ||
![]() |
002dfd5d58 | ||
![]() |
54e54b2a8a | ||
![]() |
aa768596a4 | ||
![]() |
c4958f6c54 | ||
![]() |
2c2a5314d4 | ||
![]() |
669516c60b | ||
![]() |
a56a29a6c4 | ||
![]() |
4e31f47482 | ||
![]() |
c1f14f9653 | ||
![]() |
2746c52d7b | ||
![]() |
5df323bacb | ||
![]() |
776cc26377 | ||
![]() |
bdfb6a90b6 | ||
![]() |
6db44dfab1 | ||
![]() |
c68ac7db6d | ||
![]() |
e09862e940 | ||
![]() |
c7bd7469a1 | ||
![]() |
b39857fd2e | ||
![]() |
8170f823ab | ||
![]() |
38c0ead45c | ||
![]() |
1d027c1694 | ||
![]() |
45dc21fbf7 | ||
![]() |
d4f4de234a | ||
![]() |
394d5471e3 | ||
![]() |
beb098adb3 | ||
![]() |
fda370d35b | ||
![]() |
99681e1bbb | ||
![]() |
21f44380b1 | ||
![]() |
f4c1af1bb8 | ||
![]() |
c002b81ebd | ||
![]() |
d2f07ba3b6 | ||
![]() |
24fc27b09e | ||
![]() |
cf4ff856d5 | ||
![]() |
b01de1ad6e | ||
![]() |
5fb1bcb552 | ||
![]() |
b937ba38b6 | ||
![]() |
849718fdc7 | ||
![]() |
a6372f701d | ||
![]() |
12f135bb14 | ||
![]() |
16870fcbb9 | ||
![]() |
2fbd152fb3 | ||
![]() |
a7b178e024 | ||
![]() |
c5a035437b | ||
![]() |
75c550fd19 | ||
![]() |
1b680344b1 | ||
![]() |
3329636d32 | ||
![]() |
77e9bf7c43 | ||
![]() |
40f5c9365e | ||
![]() |
850ba7efef | ||
![]() |
548369ca0b | ||
![]() |
afc0d5b7bc | ||
![]() |
02e92db59a | ||
![]() |
2b34454214 | ||
![]() |
12706119d3 | ||
![]() |
f7a762f32b | ||
![]() |
86d5d2a2cb | ||
![]() |
e56369b91a | ||
![]() |
b04aa24269 | ||
![]() |
dda86bd5de | ||
![]() |
a19ad7fd52 | ||
![]() |
0443fd808c | ||
![]() |
faf57c96fc |
@@ -3,13 +3,11 @@ jobs:
|
||||
build:
|
||||
working_directory: ~/code
|
||||
docker:
|
||||
- image: circleci/android:api-28-ndk
|
||||
- image: cimg/android:2023.12-ndk
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
- run: yes | sdkmanager --licenses || exit 0
|
||||
- run: yes | sdkmanager --update || exit 0
|
||||
- restore_cache:
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@@ -8,10 +8,9 @@
|
||||
.DS_Store
|
||||
/app/build
|
||||
/app/release
|
||||
/app/alpha
|
||||
/app/prod
|
||||
/app/alphaMainnet
|
||||
/app/prodMainnet
|
||||
/app/alphaStagenet
|
||||
/app/prodStagenet
|
||||
/app/alpha*
|
||||
/app/prod*
|
||||
/app/.cxx
|
||||
/monerujo.id
|
||||
/external-libs/VERSION
|
||||
/external-libs/include/wallet2_api.h
|
||||
|
@@ -1,4 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
project(monerujo)
|
||||
message(STATUS ABI_INFO = ${ANDROID_ABI})
|
||||
|
||||
add_library( monerujo
|
||||
@@ -13,7 +14,7 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs)
|
||||
|
||||
add_library(sodium STATIC IMPORTED)
|
||||
set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/libsodium/lib/${ANDROID_ABI}/libsodium.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libsodium.a)
|
||||
|
||||
############
|
||||
# OpenSSL
|
||||
@@ -21,11 +22,11 @@ set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
add_library(crypto STATIC IMPORTED)
|
||||
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libssl.a)
|
||||
|
||||
############
|
||||
# Boost
|
||||
@@ -33,39 +34,39 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
add_library(boost_chrono STATIC IMPORTED)
|
||||
set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_chrono.a)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_wserialization.a)
|
||||
|
||||
#############
|
||||
# Monero
|
||||
@@ -73,99 +74,107 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
add_library(wallet_api STATIC IMPORTED)
|
||||
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet_api.a)
|
||||
|
||||
add_library(wallet STATIC IMPORTED)
|
||||
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_basic.a)
|
||||
|
||||
add_library(mnemonics STATIC IMPORTED)
|
||||
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmnemonics.a)
|
||||
|
||||
add_library(common STATIC IMPORTED)
|
||||
set_target_properties(common PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcommon.a)
|
||||
|
||||
add_library(cncrypto STATIC IMPORTED)
|
||||
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcncrypto.a)
|
||||
|
||||
add_library(ringct STATIC IMPORTED)
|
||||
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct.a)
|
||||
|
||||
add_library(ringct_basic STATIC IMPORTED)
|
||||
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct_basic.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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblockchain_db.a)
|
||||
|
||||
add_library(lmdb STATIC IMPORTED)
|
||||
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/liblmdb.a)
|
||||
|
||||
add_library(easylogging STATIC IMPORTED)
|
||||
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libeasylogging.a)
|
||||
|
||||
add_library(unbound STATIC IMPORTED)
|
||||
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
|
||||
${EXTERNAL_LIBS_DIR}/${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)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libepee.a)
|
||||
|
||||
add_library(blocks STATIC IMPORTED)
|
||||
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblocks.a)
|
||||
|
||||
add_library(checkpoints STATIC IMPORTED)
|
||||
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcheckpoints.a)
|
||||
|
||||
add_library(device STATIC IMPORTED)
|
||||
set_target_properties(device PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice.a)
|
||||
|
||||
add_library(device_trezor STATIC IMPORTED)
|
||||
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice_trezor.a)
|
||||
|
||||
add_library(multisig STATIC IMPORTED)
|
||||
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmultisig.a)
|
||||
|
||||
add_library(version STATIC IMPORTED)
|
||||
set_target_properties(version PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libversion.a)
|
||||
|
||||
add_library(net STATIC IMPORTED)
|
||||
set_target_properties(net PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libnet.a)
|
||||
|
||||
add_library(hardforks STATIC IMPORTED)
|
||||
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libhardforks.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libhardforks.a)
|
||||
|
||||
add_library(randomx STATIC IMPORTED)
|
||||
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librandomx.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librandomx.a)
|
||||
|
||||
add_library(rpc_base STATIC IMPORTED)
|
||||
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librpc_base.a)
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librpc_base.a)
|
||||
|
||||
add_library(wallet-crypto STATIC IMPORTED)
|
||||
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a)
|
||||
|
||||
add_library(cryptonote_format_utils_basic STATIC IMPORTED)
|
||||
set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_format_utils_basic.a)
|
||||
|
||||
#############
|
||||
# System
|
||||
@@ -173,16 +182,23 @@ set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
find_library( log-lib log )
|
||||
|
||||
include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
|
||||
include_directories( ${EXTERNAL_LIBS_DIR}/include )
|
||||
|
||||
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
|
||||
|
||||
if(${ANDROID_ABI} STREQUAL "x86_64")
|
||||
set(EXTRA_LIBS "wallet-crypto")
|
||||
else()
|
||||
set(EXTRA_LIBS "")
|
||||
endif()
|
||||
|
||||
target_link_libraries( monerujo
|
||||
|
||||
wallet_api
|
||||
wallet
|
||||
cryptonote_core
|
||||
cryptonote_basic
|
||||
cryptonote_format_utils_basic
|
||||
mnemonics
|
||||
ringct
|
||||
ringct_basic
|
||||
@@ -203,6 +219,7 @@ target_link_libraries( monerujo
|
||||
randomx
|
||||
hardforks
|
||||
rpc_base
|
||||
${EXTRA_LIBS}
|
||||
|
||||
boost_chrono
|
||||
boost_date_time
|
||||
|
@@ -1,14 +1,15 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
ndkVersion '17.2.4988734'
|
||||
defaultConfig {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
buildToolsVersion = '35.0.0'
|
||||
compileSdk 35
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 702
|
||||
versionName "1.17.2 'Druk'"
|
||||
targetSdkVersion 35
|
||||
versionCode 4107
|
||||
versionName "4.1.7 'Exolix'"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -23,7 +24,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions 'type', 'net'
|
||||
flavorDimensions = ['type', 'net']
|
||||
productFlavors {
|
||||
mainnet {
|
||||
dimension 'net'
|
||||
@@ -57,7 +58,7 @@ android {
|
||||
applicationIdSuffix ".debug"
|
||||
}
|
||||
applicationVariants.all { variant ->
|
||||
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
|
||||
variant.buildConfigField "String", "ID_F", "\"" + getId("ID_F") + "\""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ android {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
@@ -107,47 +108,70 @@ android {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
namespace 'com.m2049r.xmrwallet'
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
all {
|
||||
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable "MissingTranslation" // Translation is crowd-sourced.
|
||||
}
|
||||
}
|
||||
|
||||
def getId(name) {
|
||||
def Properties props = new Properties()
|
||||
static def getId(name) {
|
||||
Properties props = new Properties()
|
||||
props.load(new FileInputStream(new File('monerujo.id')))
|
||||
return props[name]
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.21'))
|
||||
|
||||
implementation 'androidx.core:core:1.16.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
|
||||
implementation 'com.google.guava:guava:33.4.8-android'
|
||||
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
implementation "com.squareup.okhttp3:okhttp:4.9.0"
|
||||
implementation "com.burgstaller:okhttp-digest:2.1"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||
implementation 'io.github.rburgst:okhttp-digest:3.1.1'
|
||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||
|
||||
implementation 'com.nulab-inc:zxcvbn:1.3.0'
|
||||
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
|
||||
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
|
||||
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
|
||||
implementation 'com.nulab-inc:zxcvbn:1.9.0'
|
||||
|
||||
implementation 'dnsjava:dnsjava:2.1.9'
|
||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
||||
implementation 'dnsjava:dnsjava:3.6.3'
|
||||
implementation 'org.slf4j:slf4j-nop:2.0.17'
|
||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||
|
||||
implementation 'com.github.aelstad:keccakj:1.1.0'
|
||||
//noinspection GradleDependency
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "org.mockito:mockito-all:1.10.19"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0"
|
||||
testImplementation 'org.json:json:20250517'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
||||
testImplementation 'org.json:json:20180813'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.4'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok:1.18.16'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
||||
compileOnly 'org.projectlombok:lombok:1.18.38'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
||||
}
|
||||
|
@@ -1,33 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.m2049r.xmrwallet">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.intent.action.START" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.intent.action.STATUS" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.REQUEST_V3_ONION_SERVICE" />
|
||||
</intent>
|
||||
|
||||
<package android:name="org.torproject.android" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:name=".XmrWalletApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:preserveLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/MyMaterialTheme"
|
||||
android:theme="@style/MyMaterialThemeClassic"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
@@ -36,27 +62,28 @@
|
||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||
android:label="@string/wallet_activity_name"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="behind"/>
|
||||
android:screenOrientation="behind" />
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="locked">
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:scheme="monero" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:scheme="bitcoin" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -77,7 +104,12 @@
|
||||
android:name=".service.WalletService"
|
||||
android:description="@string/service_description"
|
||||
android:exported="false"
|
||||
android:label="Monero Wallet Service" />
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:label="Monero Wallet Service">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="Keeps app in sync with the blockchain" />
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
@@ -89,4 +121,5 @@
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
</manifest>
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017 m2049r
|
||||
* Copyright (c) 2017-2024 m2049r
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
#include <android/log.h>
|
||||
|
||||
@@ -28,6 +30,10 @@
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
*/
|
||||
|
||||
void ThrowException(JNIEnv *jenv, const char* type, const char* msg) {
|
||||
jenv->ThrowNew(jenv->FindClass(type), msg);
|
||||
}
|
||||
|
||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
jclass c = env->GetObjectClass(obj);
|
||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||
@@ -35,8 +41,16 @@ jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handl
|
||||
|
||||
template<typename T>
|
||||
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
return reinterpret_cast<T *>(env->GetLongField(obj, getHandleField(env, obj, fieldName)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void destroyNativeObject(JNIEnv *env, T nativeObjectHandle, jobject obj, const char *fieldName = "handle") {
|
||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
||||
return reinterpret_cast<T *>(handle);
|
||||
if (handle != 0) {
|
||||
ThrowException(env, "java/lang/IllegalStateException", "invalid handle (destroy)");
|
||||
}
|
||||
delete reinterpret_cast<T *>(nativeObjectHandle);
|
||||
}
|
||||
|
||||
void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
||||
@@ -54,7 +68,7 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern const char* const MONERO_VERSION; // the actual monero core version
|
||||
extern const char *const MONERO_VERSION; // the actual monero core version
|
||||
|
||||
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||
enum {
|
||||
@@ -62,18 +76,40 @@ enum {
|
||||
HASH_DATA_AREA = 136
|
||||
};
|
||||
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height);
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed,
|
||||
uint64_t height);
|
||||
|
||||
inline void slow_hash(const void *data, const size_t length, char *hash) {
|
||||
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
|
||||
}
|
||||
|
||||
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
||||
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/, 0 /*height*/);
|
||||
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/,
|
||||
0 /*height*/);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Monerujo {
|
||||
class SidekickWallet {
|
||||
public:
|
||||
enum Status {
|
||||
Status_Ok,
|
||||
Status_Error,
|
||||
Status_Critical
|
||||
};
|
||||
|
||||
SidekickWallet(uint8_t networkType, std::string a, std::string b);
|
||||
|
||||
~SidekickWallet();
|
||||
|
||||
std::string call(int commandId, const std::string &request);
|
||||
|
||||
void reset();
|
||||
|
||||
Status status() const;
|
||||
|
||||
};
|
||||
}
|
||||
#endif //XMRWALLET_WALLET_LIB_H
|
||||
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@@ -20,8 +20,6 @@
|
||||
|
||||
package com.btchip.comm;
|
||||
|
||||
import com.btchip.BTChipException;
|
||||
|
||||
public interface BTChipTransport {
|
||||
byte[] exchange(byte[] command);
|
||||
|
||||
|
@@ -28,7 +28,6 @@ import android.hardware.usb.UsbInterface;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.hardware.usb.UsbRequest;
|
||||
|
||||
import com.btchip.BTChipException;
|
||||
import com.btchip.comm.BTChipTransport;
|
||||
import com.btchip.comm.LedgerHelper;
|
||||
import com.btchip.utils.Dump;
|
||||
@@ -78,7 +77,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||
}
|
||||
|
||||
private static final int VID = 0x2C97;
|
||||
private static final int[] PID_HIDS = {0x0001, 0x0004};
|
||||
private static final int[] PID_HIDS = {0x0001, 0x0004, 0x0005};
|
||||
|
||||
private UsbDeviceConnection connection;
|
||||
private UsbInterface dongleInterface;
|
||||
|
@@ -77,7 +77,7 @@ public class Dispatcher implements PeerRetriever.OnGetPeers {
|
||||
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
|
||||
Timber.d("Retrieved %s", nodeInfo);
|
||||
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
|
||||
nodeInfo.setName();
|
||||
nodeInfo.setDefaultName();
|
||||
rpcNodes.add(nodeInfo);
|
||||
Timber.d("RPC: %s", nodeInfo);
|
||||
// the following is not totally correct but it works (otherwise we need to
|
||||
|
File diff suppressed because it is too large
Load Diff
326
app/src/main/java/com/m2049r/xmrwallet/BluetoothFragment.java
Normal file
326
app/src/main/java/com/m2049r/xmrwallet/BluetoothFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
38
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
38
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class LockFragment extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Timber.d("onCreateView");
|
||||
final FrameLayout frame = new FrameLayout(requireContext());
|
||||
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return frame;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,6 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
|
||||
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
|
||||
|
@@ -20,7 +20,6 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -32,6 +31,8 @@ import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -59,7 +60,6 @@ import java.text.NumberFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import timber.log.Timber;
|
||||
@@ -69,7 +69,7 @@ public class NodeFragment extends Fragment
|
||||
|
||||
static private int NODES_TO_FIND = 10;
|
||||
|
||||
static private NumberFormat FORMATTER = NumberFormat.getInstance();
|
||||
static private final NumberFormat FORMATTER = NumberFormat.getInstance();
|
||||
|
||||
private SwipeRefreshLayout pullToRefresh;
|
||||
private TextView tvPull;
|
||||
@@ -105,7 +105,7 @@ public class NodeFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Listener) {
|
||||
this.activityCallback = (Listener) context;
|
||||
@@ -147,6 +147,13 @@ public class NodeFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
Toast.makeText(requireActivity(), getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -189,6 +196,7 @@ public class NodeFragment extends Fragment
|
||||
|
||||
private boolean refresh(int type) {
|
||||
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
|
||||
onBackPressedCallback.setEnabled(true);
|
||||
asyncFindNodes = new AsyncFindNodes();
|
||||
updateRefreshElements();
|
||||
asyncFindNodes.execute(type);
|
||||
@@ -199,10 +207,11 @@ public class NodeFragment extends Fragment
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.node_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
@@ -219,8 +228,8 @@ public class NodeFragment extends Fragment
|
||||
activityCallback.setNode(nodeItem); // this marks it as selected & saves it as well
|
||||
nodeItem.setSelecting(false);
|
||||
try {
|
||||
Objects.requireNonNull(getActivity()).runOnUiThread(() -> nodesAdapter.allowClick(true));
|
||||
} catch (NullPointerException ex) {
|
||||
requireActivity().runOnUiThread(() -> nodesAdapter.allowClick(true));
|
||||
} catch (IllegalStateException ex) {
|
||||
// it's ok
|
||||
}
|
||||
});
|
||||
@@ -228,24 +237,23 @@ public class NodeFragment extends Fragment
|
||||
|
||||
// open up edit dialog
|
||||
@Override
|
||||
public void onLongInteraction(final View view, final NodeInfo nodeItem) {
|
||||
public boolean onLongInteraction(final View view, final NodeInfo nodeItem) {
|
||||
Timber.d("onLongInteraction");
|
||||
EditDialog diag = createEditDialog(nodeItem);
|
||||
if (diag != null) {
|
||||
diag.show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
switch (id) {
|
||||
case R.id.fab:
|
||||
EditDialog diag = createEditDialog(null);
|
||||
if (diag != null) {
|
||||
diag.show();
|
||||
}
|
||||
break;
|
||||
if (id == R.id.fab) {
|
||||
EditDialog diag = createEditDialog(null);
|
||||
if (diag != null) {
|
||||
diag.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,8 +290,7 @@ public class NodeFragment extends Fragment
|
||||
} else if (params[0] == SCAN) {
|
||||
// otherwise scan the network
|
||||
Timber.d("scanning");
|
||||
Set<NodeInfo> seedList = new HashSet<>();
|
||||
seedList.addAll(nodeList);
|
||||
Set<NodeInfo> seedList = new HashSet<>(nodeList);
|
||||
nodeList.clear();
|
||||
Timber.d("seed %d", seedList.size());
|
||||
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
||||
@@ -345,6 +352,7 @@ public class NodeFragment extends Fragment
|
||||
|
||||
private void complete() {
|
||||
asyncFindNodes = null;
|
||||
onBackPressedCallback.setEnabled(false);
|
||||
if (!isAdded()) return;
|
||||
//if (isCancelled()) return;
|
||||
tvPull.setText(getString(R.string.node_pull_hint));
|
||||
@@ -404,16 +412,12 @@ public class NodeFragment extends Fragment
|
||||
etNodeHost.setError(getString(R.string.node_host_empty));
|
||||
return false;
|
||||
}
|
||||
final boolean setHostSuccess = Helper.runWithNetwork(new Helper.Action() {
|
||||
@Override
|
||||
public boolean run() {
|
||||
try {
|
||||
nodeInfo.setHost(host);
|
||||
return true;
|
||||
} catch (UnknownHostException ex) {
|
||||
etNodeHost.setError(getString(R.string.node_host_unresolved));
|
||||
return false;
|
||||
}
|
||||
final boolean setHostSuccess = Helper.runWithNetwork(() -> {
|
||||
try {
|
||||
nodeInfo.setHost(host);
|
||||
return true;
|
||||
} catch (UnknownHostException ex) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (!setHostSuccess) {
|
||||
@@ -451,7 +455,7 @@ public class NodeFragment extends Fragment
|
||||
|
||||
private void closeDialog() {
|
||||
if (editDialog == null) throw new IllegalStateException();
|
||||
Helper.hideKeyboardAlways(getActivity());
|
||||
Helper.hideKeyboardAlways(requireActivity());
|
||||
editDialog.dismiss();
|
||||
editDialog = null;
|
||||
NodeFragment.this.editDialog = null;
|
||||
@@ -524,7 +528,7 @@ public class NodeFragment extends Fragment
|
||||
.setNegativeButton(getString(R.string.label_cancel),
|
||||
(dialog, id) -> {
|
||||
closeDialog();
|
||||
nodesAdapter.dataSetChanged(); // to refresh test results
|
||||
nodesAdapter.setNodes(); // to refresh test results
|
||||
});
|
||||
|
||||
editDialog = alertDialogBuilder.create();
|
||||
@@ -533,20 +537,10 @@ public class NodeFragment extends Fragment
|
||||
@Override
|
||||
public void onShow(final DialogInterface dialog) {
|
||||
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||
testButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
test();
|
||||
}
|
||||
});
|
||||
testButton.setOnClickListener(view -> test());
|
||||
|
||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
apply();
|
||||
}
|
||||
});
|
||||
button.setOnClickListener(view -> apply());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -554,15 +548,13 @@ public class NodeFragment extends Fragment
|
||||
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
||||
test();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
etNodePass.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
||||
test();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -588,11 +580,13 @@ public class NodeFragment extends Fragment
|
||||
if (nodeBackup == null) {
|
||||
nodesAdapter.addNode(nodeInfo);
|
||||
} else {
|
||||
nodesAdapter.dataSetChanged();
|
||||
nodesAdapter.setNodes();
|
||||
}
|
||||
nodesAdapter.notifyItemChanged(nodeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void restoreDefaultNodes() {
|
||||
|
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2021 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
|
||||
public interface OnBlockUpdateListener {
|
||||
void onBlockUpdate(final Wallet wallet);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,14 @@ package com.m2049r.xmrwallet;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
@@ -42,7 +44,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
||||
private ZXingScannerView mScannerView;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Timber.d("onCreateView");
|
||||
mScannerView = new ZXingScannerView(getActivity());
|
||||
return mScannerView;
|
||||
@@ -73,14 +75,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
||||
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
|
||||
// * I don't know why this is the case but I don't have the time to figure out.
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new
|
||||
|
||||
Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mScannerView.resumeCameraPreview(ScannerFragment.this);
|
||||
}
|
||||
}, 2000);
|
||||
handler.postDelayed(() -> mScannerView.resumeCameraPreview(ScannerFragment.this), 2000);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +86,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnScannedListener) {
|
||||
this.onScannedListener = (OnScannedListener) context;
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
@@ -29,8 +31,6 @@ import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
|
||||
public abstract class SecureActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -66,11 +66,7 @@ public abstract class SecureActivity extends AppCompatActivity {
|
||||
|
||||
Locale locale = LocaleHelper.getPreferredLocale(this);
|
||||
if (locale != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
config.setLocale(locale);
|
||||
} else {
|
||||
config.locale = locale;
|
||||
}
|
||||
config.setLocale(locale);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
126
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
126
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.m2049r.xmrwallet.dialog.AboutFragment;
|
||||
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
|
||||
findPreference(getString(R.string.about_info)).setOnPreferenceClickListener(preference -> {
|
||||
AboutFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
findPreference(getString(R.string.privacy_info)).setOnPreferenceClickListener(preference -> {
|
||||
PrivacyFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
findPreference(getString(R.string.credits_info)).setOnPreferenceClickListener(preference -> {
|
||||
CreditsFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(getString(R.string.preferred_locale))) {
|
||||
activity.recreate();
|
||||
} else if (key.equals(getString(R.string.preferred_nightmode))) {
|
||||
NightmodeHelper.setNightMode(DayNightMode.valueOf(sharedPreferences.getString(key, "AUTO")));
|
||||
} else if (key.equals(getString(R.string.preferred_theme))) {
|
||||
ThemeHelper.setTheme((Activity) activity, sharedPreferences.getString(key, "Classic"));
|
||||
activity.recreate();
|
||||
}
|
||||
}
|
||||
|
||||
private SettingsFragment.Listener activity;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof SettingsFragment.Listener) {
|
||||
activity = (SettingsFragment.Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context + " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Timber.d("onResume()");
|
||||
activity.setSubtitle(getString(R.string.menu_settings));
|
||||
activity.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||
populateLanguages();
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void setSubtitle(String title);
|
||||
|
||||
void recreate();
|
||||
|
||||
void setTheme(@StyleRes final int resId);
|
||||
}
|
||||
|
||||
public void populateLanguages() {
|
||||
ListPreference language = findPreference(getString(R.string.preferred_locale));
|
||||
assert language != null;
|
||||
|
||||
final ArrayList<Locale> availableLocales = LocaleHelper.getAvailableLocales(requireContext());
|
||||
Collections.sort(availableLocales, (locale1, locale2) -> {
|
||||
String localeString1 = LocaleHelper.getDisplayName(locale1, true);
|
||||
String localeString2 = LocaleHelper.getDisplayName(locale2, true);
|
||||
return localeString1.compareTo(localeString2);
|
||||
});
|
||||
|
||||
String[] localeDisplayNames = new String[1 + availableLocales.size()];
|
||||
localeDisplayNames[0] = getString(R.string.language_system_default);
|
||||
for (int i = 1; i < localeDisplayNames.length; i++) {
|
||||
localeDisplayNames[i] = LocaleHelper.getDisplayName(availableLocales.get(i - 1), true);
|
||||
}
|
||||
language.setEntries(localeDisplayNames);
|
||||
|
||||
String[] languageTags = new String[1 + availableLocales.size()];
|
||||
languageTags[0] = "";
|
||||
for (int i = 1; i < languageTags.length; i++) {
|
||||
languageTags[i] = availableLocales.get(i - 1).toLanguageTag();
|
||||
}
|
||||
language.setEntryValues(languageTags);
|
||||
}
|
||||
}
|
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2018 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.m2049r.xmrwallet.data.BluetoothInfo;
|
||||
import com.m2049r.xmrwallet.layout.BluetoothInfoAdapter;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SidekickConnectFragment extends Fragment
|
||||
implements BluetoothInfoAdapter.OnInteractionListener {
|
||||
|
||||
private BluetoothAdapter bluetoothAdapter;
|
||||
|
||||
private SwipeRefreshLayout pullToRefresh;
|
||||
|
||||
private BluetoothInfoAdapter infoAdapter;
|
||||
|
||||
private Listener activityCallback;
|
||||
|
||||
public interface Listener {
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void setSubtitle(String title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Listener) {
|
||||
this.activityCallback = (Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context + " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Timber.d("onPause()");
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (bluetoothAdapter.isDiscovering()) {
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
}
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Timber.d("onResume()");
|
||||
activityCallback.setSubtitle(getString(R.string.label_bluetooth));
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
||||
assert btFragment != null;
|
||||
btFragment.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
Timber.d("onCreateView");
|
||||
View view = inflater.inflate(R.layout.fragment_sidekick_connect, container, false);
|
||||
|
||||
RecyclerView recyclerView = view.findViewById(R.id.list);
|
||||
infoAdapter = new BluetoothInfoAdapter(this);
|
||||
recyclerView.setAdapter(infoAdapter);
|
||||
|
||||
pullToRefresh = view.findViewById(R.id.pullToRefresh);
|
||||
pullToRefresh.setOnRefreshListener(() -> {
|
||||
populateList();
|
||||
pullToRefresh.setRefreshing(false);
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void populateList() {
|
||||
List<BluetoothInfo> items = new ArrayList<>();
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
|
||||
final int deviceCLass = device.getBluetoothClass().getDeviceClass();
|
||||
switch (deviceCLass) {
|
||||
case BluetoothClass.Device.PHONE_SMART:
|
||||
//TODO verify these are correct
|
||||
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
|
||||
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
|
||||
items.add(new BluetoothInfo(device));
|
||||
}
|
||||
}
|
||||
infoAdapter.setItems(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
// Get the local Bluetooth adapter
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
populateList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.sidekick_connect_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// Make sure we're not doing discovery anymore
|
||||
if (bluetoothAdapter != null) {
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInteraction(final View view, final BluetoothInfo item) {
|
||||
Timber.d("onInteraction %s", item);
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||
throw new IllegalStateException("Bluetooth permission not granted");
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
|
||||
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
||||
assert btFragment != null;
|
||||
btFragment.connectDevice(item.getAddress());
|
||||
}
|
||||
|
||||
public void allowClick() {
|
||||
infoAdapter.allowClick(true);
|
||||
}
|
||||
}
|
242
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal file
242
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.m2049r.xmrwallet.data.Subaddress;
|
||||
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
|
||||
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SubaddressFragment extends Fragment implements SubaddressInfoAdapter.OnInteractionListener,
|
||||
View.OnClickListener, OnBlockUpdateListener {
|
||||
static public final String KEY_MODE = "mode";
|
||||
static public final String MODE_MANAGER = "manager";
|
||||
|
||||
private SubaddressInfoAdapter adapter;
|
||||
|
||||
private Listener activityCallback;
|
||||
|
||||
private Wallet wallet;
|
||||
|
||||
// Container Activity must implement this interface
|
||||
public interface Listener {
|
||||
void onSubaddressSelected(Subaddress subaddress);
|
||||
|
||||
void setSubtitle(String title);
|
||||
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void showSubaddress(View view, final int subaddressIndex);
|
||||
|
||||
void saveWallet();
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
void showProgressDialog(int msgId);
|
||||
|
||||
void showLedgerProgressDialog(int mode);
|
||||
|
||||
void dismissProgressDialog();
|
||||
}
|
||||
|
||||
private ProgressListener progressCallback = null;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof ProgressListener) {
|
||||
progressCallback = (ProgressListener) context;
|
||||
}
|
||||
if (context instanceof Listener) {
|
||||
activityCallback = (Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Timber.d("onPause()");
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
Timber.d("onCreateView");
|
||||
|
||||
final Bundle b = getArguments();
|
||||
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
|
||||
|
||||
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
|
||||
view.findViewById(R.id.fab).setOnClickListener(this);
|
||||
|
||||
if (managerMode) {
|
||||
view.findViewById(R.id.tvInstruction).setVisibility(View.GONE);
|
||||
view.findViewById(R.id.tvHint).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
final RecyclerView list = view.findViewById(R.id.list);
|
||||
adapter = new SubaddressInfoAdapter(getActivity(), this);
|
||||
list.setAdapter(adapter);
|
||||
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
list.scrollToPosition(positionStart);
|
||||
}
|
||||
});
|
||||
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
wallet = WalletManager.getInstance().getWallet();
|
||||
|
||||
loadList();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
public void loadList() {
|
||||
Timber.d("loadList()");
|
||||
final int numSubaddresses = wallet.getNumSubaddresses();
|
||||
final List<Subaddress> list = new ArrayList<>();
|
||||
for (int i = 0; i < numSubaddresses; i++) {
|
||||
list.add(wallet.getSubaddressObject(i));
|
||||
}
|
||||
adapter.setInfos(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockUpdate(Wallet wallet) {
|
||||
loadList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.fab) {
|
||||
getNewSubaddress();
|
||||
}
|
||||
}
|
||||
|
||||
private int lastUsedSubaddress() {
|
||||
int lastUsedSubaddress = 0;
|
||||
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
||||
if (info.addressIndex > lastUsedSubaddress)
|
||||
lastUsedSubaddress = info.addressIndex;
|
||||
}
|
||||
return lastUsedSubaddress;
|
||||
}
|
||||
|
||||
private void getNewSubaddress() {
|
||||
final int maxSubaddresses = lastUsedSubaddress() + wallet.getDeviceType().getSubaddressLookahead();
|
||||
if (wallet.getNumSubaddresses() < maxSubaddresses)
|
||||
new AsyncSubaddress().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||
else
|
||||
Toast.makeText(getActivity(), getString(R.string.max_subaddress_warning), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@RequiredArgsConstructor
|
||||
private class AsyncSubaddress extends AsyncTask<Void, Void, Boolean> {
|
||||
boolean dialogOpened = false;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
if ((wallet.getDeviceType() == Wallet.Device.Ledger) && (progressCallback != null)) {
|
||||
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
||||
dialogOpened = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
if (params.length != 0) return false;
|
||||
wallet.getNewSubaddress();
|
||||
if (activityCallback != null) {
|
||||
activityCallback.saveWallet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
super.onPostExecute(result);
|
||||
if (dialogOpened)
|
||||
progressCallback.dismissProgressDialog();
|
||||
if (!isAdded()) // never mind then
|
||||
return;
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
boolean managerMode = false;
|
||||
|
||||
// Callbacks from SubaddressInfoAdapter
|
||||
@Override
|
||||
public void onInteraction(final View view, @NonNull final Subaddress subaddress) {
|
||||
if (managerMode)
|
||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||
else
|
||||
activityCallback.onSubaddressSelected(subaddress); // also closes the fragment with onBackpressed()
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongInteraction(View view, Subaddress subaddress) {
|
||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionInflater;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.m2049r.xmrwallet.data.Subaddress;
|
||||
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SubaddressInfoFragment extends Fragment
|
||||
implements TransactionInfoAdapter.Listener, OnBlockUpdateListener {
|
||||
private TransactionInfoAdapter adapter;
|
||||
|
||||
private Subaddress subaddress;
|
||||
|
||||
private TextInputLayout etName;
|
||||
private TextView tvTxLabel;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
||||
|
||||
etName = view.findViewById(R.id.etName);
|
||||
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
||||
|
||||
final RecyclerView list = view.findViewById(R.id.list);
|
||||
adapter = new TransactionInfoAdapter(getActivity(), this);
|
||||
list.setAdapter(adapter);
|
||||
|
||||
final Wallet wallet = activityCallback.getWallet();
|
||||
|
||||
Bundle b = getArguments();
|
||||
assert b != null;
|
||||
final int subaddressIndex = b.getInt("subaddressIndex");
|
||||
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
||||
|
||||
etName.getEditText().setText(subaddress.getDisplayLabel());
|
||||
final TextView tvAddress = view.findViewById(R.id.tvAddress);
|
||||
tvAddress.setText(requireContext().getString(R.string.subbaddress_info_subtitle,
|
||||
subaddress.getAddressIndex(), subaddress.getAddress()));
|
||||
|
||||
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
|
||||
}
|
||||
});
|
||||
etName.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
Helper.hideKeyboard(getActivity());
|
||||
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
|
||||
onRefreshed(wallet);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
onRefreshed(wallet);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Transition transform = TransitionInflater.from(requireContext())
|
||||
.inflateTransition(R.transition.details);
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
public void onRefreshed(final Wallet wallet) {
|
||||
Timber.d("onRefreshed");
|
||||
List<TransactionInfo> list = new ArrayList<>();
|
||||
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
||||
if (info.addressIndex == subaddress.getAddressIndex())
|
||||
list.add(info);
|
||||
}
|
||||
adapter.setInfos(list);
|
||||
if (list.isEmpty())
|
||||
tvTxLabel.setText(R.string.subaddress_notx_label);
|
||||
else
|
||||
tvTxLabel.setText(R.string.subaddress_tx_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockUpdate(Wallet wallet) {
|
||||
onRefreshed(wallet);
|
||||
}
|
||||
|
||||
// Callbacks from TransactionInfoAdapter
|
||||
@Override
|
||||
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||
activityCallback.onTxDetailsRequest(view, infoItem);
|
||||
}
|
||||
|
||||
Listener activityCallback;
|
||||
|
||||
// Container Activity must implement this interface
|
||||
public interface Listener {
|
||||
void onTxDetailsRequest(View view, TransactionInfo info);
|
||||
|
||||
Wallet getWallet();
|
||||
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void setTitle(String title, String subtitle);
|
||||
|
||||
void setSubtitle(String subtitle);
|
||||
|
||||
long getDaemonHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Listener) {
|
||||
this.activityCallback = (Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Timber.d("onResume()");
|
||||
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDaemonHeight() {
|
||||
return activityCallback.getDaemonHeight();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user