mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 00:53:36 +02:00
Compare commits
178 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 |
@@ -3,13 +3,11 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
working_directory: ~/code
|
working_directory: ~/code
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-28-ndk
|
- image: cimg/android:2023.12-ndk
|
||||||
environment:
|
environment:
|
||||||
JVM_OPTS: -Xmx3200m
|
JVM_OPTS: -Xmx3200m
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: yes | sdkmanager --licenses || exit 0
|
|
||||||
- run: yes | sdkmanager --update || exit 0
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||||
- run:
|
- run:
|
||||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -8,11 +8,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
/app/build
|
/app/build
|
||||||
/app/release
|
/app/release
|
||||||
/app/alpha
|
/app/alpha*
|
||||||
/app/prod
|
/app/prod*
|
||||||
/app/alphaMainnet
|
|
||||||
/app/prodMainnet
|
|
||||||
/app/alphaStagenet
|
|
||||||
/app/prodStagenet
|
|
||||||
/app/.cxx
|
/app/.cxx
|
||||||
/monerujo.id
|
/monerujo.id
|
||||||
|
/external-libs/VERSION
|
||||||
|
/external-libs/include/wallet2_api.h
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
|
project(monerujo)
|
||||||
message(STATUS ABI_INFO = ${ANDROID_ABI})
|
message(STATUS ABI_INFO = ${ANDROID_ABI})
|
||||||
|
|
||||||
add_library( monerujo
|
add_library( monerujo
|
||||||
@@ -121,7 +122,7 @@ set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
|
|||||||
|
|
||||||
add_library(unbound STATIC IMPORTED)
|
add_library(unbound STATIC IMPORTED)
|
||||||
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
||||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libunbound.a)
|
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libunbound.a)
|
||||||
|
|
||||||
add_library(epee STATIC IMPORTED)
|
add_library(epee STATIC IMPORTED)
|
||||||
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
|
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
|
||||||
@@ -171,6 +172,10 @@ add_library(wallet-crypto STATIC IMPORTED)
|
|||||||
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
|
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
|
||||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a)
|
${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
|
# System
|
||||||
#############
|
#############
|
||||||
@@ -193,6 +198,7 @@ target_link_libraries( monerujo
|
|||||||
wallet
|
wallet
|
||||||
cryptonote_core
|
cryptonote_core
|
||||||
cryptonote_basic
|
cryptonote_basic
|
||||||
|
cryptonote_format_utils_basic
|
||||||
mnemonics
|
mnemonics
|
||||||
ringct
|
ringct
|
||||||
ringct_basic
|
ringct_basic
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
ndkVersion '17.2.4988734'
|
||||||
buildToolsVersion '29.0.3'
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
|
buildToolsVersion = '35.0.0'
|
||||||
|
compileSdk 35
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 35
|
||||||
versionCode 800
|
versionCode 4107
|
||||||
versionName "1.18.0 'ChAdOx1'"
|
versionName "4.1.7 'Exolix'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -23,7 +24,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions 'type', 'net'
|
flavorDimensions = ['type', 'net']
|
||||||
productFlavors {
|
productFlavors {
|
||||||
mainnet {
|
mainnet {
|
||||||
dimension 'net'
|
dimension 'net'
|
||||||
@@ -57,7 +58,7 @@ android {
|
|||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
}
|
}
|
||||||
applicationVariants.all { variant ->
|
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 {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
||||||
universalApk true
|
universalApk true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,47 +108,70 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
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) {
|
static def getId(name) {
|
||||||
def Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
props.load(new FileInputStream(new File('monerujo.id')))
|
props.load(new FileInputStream(new File('monerujo.id')))
|
||||||
return props[name]
|
return props[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.core:core:1.3.2'
|
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.21'))
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
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.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.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
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 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
implementation "com.squareup.okhttp3:okhttp:4.9.0"
|
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||||
implementation "com.burgstaller:okhttp-digest:2.1"
|
implementation 'io.github.rburgst:okhttp-digest:3.1.1'
|
||||||
implementation "com.jakewharton.timber:timber:4.7.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 'dnsjava:dnsjava:3.6.3'
|
||||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
implementation 'org.slf4j:slf4j-nop:2.0.17'
|
||||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
|
||||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
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"
|
compileOnly 'org.projectlombok:lombok:1.18.38'
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
||||||
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'
|
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.m2049r.xmrwallet">
|
|
||||||
|
|
||||||
|
<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.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<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.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<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" />
|
||||||
|
<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
|
<application
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:name=".XmrWalletApplication"
|
android:name=".XmrWalletApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:preserveLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/MyMaterialTheme"
|
android:theme="@style/MyMaterialThemeClassic"
|
||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||||
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="portrait">
|
android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@@ -36,27 +62,28 @@
|
|||||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||||
android:label="@string/wallet_activity_name"
|
android:label="@string/wallet_activity_name"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:screenOrientation="behind"/>
|
android:screenOrientation="behind" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".LoginActivity"
|
android:name=".LoginActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="locked">
|
android:screenOrientation="locked">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter android:label="@string/app_name">
|
<intent-filter android:label="@string/app_name">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<data android:scheme="monero" />
|
<data android:scheme="monero" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter android:label="@string/app_name">
|
<intent-filter android:label="@string/app_name">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<data android:scheme="bitcoin" />
|
<data android:scheme="bitcoin" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
@@ -77,7 +104,12 @@
|
|||||||
android:name=".service.WalletService"
|
android:name=".service.WalletService"
|
||||||
android:description="@string/service_description"
|
android:description="@string/service_description"
|
||||||
android:exported="false"
|
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
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
@@ -89,4 +121,5 @@
|
|||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths" />
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</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>
|
* <p>
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
@@ -28,6 +30,10 @@
|
|||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
#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") {
|
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||||
jclass c = env->GetObjectClass(obj);
|
jclass c = env->GetObjectClass(obj);
|
||||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||||
@@ -35,8 +41,16 @@ jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handl
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||||
|
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));
|
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) {
|
void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
||||||
@@ -54,7 +68,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#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
|
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||||
enum {
|
enum {
|
||||||
@@ -62,18 +76,40 @@ enum {
|
|||||||
HASH_DATA_AREA = 136
|
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) {
|
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*/);
|
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#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;
|
package com.btchip.comm;
|
||||||
|
|
||||||
import com.btchip.BTChipException;
|
|
||||||
|
|
||||||
public interface BTChipTransport {
|
public interface BTChipTransport {
|
||||||
byte[] exchange(byte[] command);
|
byte[] exchange(byte[] command);
|
||||||
|
|
||||||
|
@@ -28,7 +28,6 @@ import android.hardware.usb.UsbInterface;
|
|||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.hardware.usb.UsbRequest;
|
import android.hardware.usb.UsbRequest;
|
||||||
|
|
||||||
import com.btchip.BTChipException;
|
|
||||||
import com.btchip.comm.BTChipTransport;
|
import com.btchip.comm.BTChipTransport;
|
||||||
import com.btchip.comm.LedgerHelper;
|
import com.btchip.comm.LedgerHelper;
|
||||||
import com.btchip.utils.Dump;
|
import com.btchip.utils.Dump;
|
||||||
@@ -78,7 +77,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final int VID = 0x2C97;
|
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 UsbDeviceConnection connection;
|
||||||
private UsbInterface dongleInterface;
|
private UsbInterface dongleInterface;
|
||||||
|
@@ -77,7 +77,7 @@ public class Dispatcher implements PeerRetriever.OnGetPeers {
|
|||||||
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
|
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
|
||||||
Timber.d("Retrieved %s", nodeInfo);
|
Timber.d("Retrieved %s", nodeInfo);
|
||||||
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
|
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
|
||||||
nodeInfo.setName();
|
nodeInfo.setDefaultName();
|
||||||
rpcNodes.add(nodeInfo);
|
rpcNodes.add(nodeInfo);
|
||||||
Timber.d("RPC: %s", nodeInfo);
|
Timber.d("RPC: %s", nodeInfo);
|
||||||
// the following is not totally correct but it works (otherwise we need to
|
// 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 android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
|
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
|
||||||
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
|
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
|
||||||
|
@@ -20,7 +20,6 @@ import android.content.Context;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -32,6 +31,8 @@ import android.widget.Button;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -59,7 +60,6 @@ import java.text.NumberFormat;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -69,7 +69,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
static private int NODES_TO_FIND = 10;
|
static private int NODES_TO_FIND = 10;
|
||||||
|
|
||||||
static private NumberFormat FORMATTER = NumberFormat.getInstance();
|
static private final NumberFormat FORMATTER = NumberFormat.getInstance();
|
||||||
|
|
||||||
private SwipeRefreshLayout pullToRefresh;
|
private SwipeRefreshLayout pullToRefresh;
|
||||||
private TextView tvPull;
|
private TextView tvPull;
|
||||||
@@ -105,7 +105,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof Listener) {
|
if (context instanceof Listener) {
|
||||||
this.activityCallback = (Listener) context;
|
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
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
@@ -189,6 +196,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private boolean refresh(int type) {
|
private boolean refresh(int type) {
|
||||||
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
|
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
asyncFindNodes = new AsyncFindNodes();
|
asyncFindNodes = new AsyncFindNodes();
|
||||||
updateRefreshElements();
|
updateRefreshElements();
|
||||||
asyncFindNodes.execute(type);
|
asyncFindNodes.execute(type);
|
||||||
@@ -199,10 +207,11 @@ public class NodeFragment extends Fragment
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.node_menu, menu);
|
inflater.inflate(R.menu.node_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
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
|
activityCallback.setNode(nodeItem); // this marks it as selected & saves it as well
|
||||||
nodeItem.setSelecting(false);
|
nodeItem.setSelecting(false);
|
||||||
try {
|
try {
|
||||||
Objects.requireNonNull(getActivity()).runOnUiThread(() -> nodesAdapter.allowClick(true));
|
requireActivity().runOnUiThread(() -> nodesAdapter.allowClick(true));
|
||||||
} catch (NullPointerException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
// it's ok
|
// it's ok
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -281,8 +290,7 @@ public class NodeFragment extends Fragment
|
|||||||
} else if (params[0] == SCAN) {
|
} else if (params[0] == SCAN) {
|
||||||
// otherwise scan the network
|
// otherwise scan the network
|
||||||
Timber.d("scanning");
|
Timber.d("scanning");
|
||||||
Set<NodeInfo> seedList = new HashSet<>();
|
Set<NodeInfo> seedList = new HashSet<>(nodeList);
|
||||||
seedList.addAll(nodeList);
|
|
||||||
nodeList.clear();
|
nodeList.clear();
|
||||||
Timber.d("seed %d", seedList.size());
|
Timber.d("seed %d", seedList.size());
|
||||||
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
||||||
@@ -344,6 +352,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private void complete() {
|
private void complete() {
|
||||||
asyncFindNodes = null;
|
asyncFindNodes = null;
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
//if (isCancelled()) return;
|
//if (isCancelled()) return;
|
||||||
tvPull.setText(getString(R.string.node_pull_hint));
|
tvPull.setText(getString(R.string.node_pull_hint));
|
||||||
@@ -403,16 +412,12 @@ public class NodeFragment extends Fragment
|
|||||||
etNodeHost.setError(getString(R.string.node_host_empty));
|
etNodeHost.setError(getString(R.string.node_host_empty));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final boolean setHostSuccess = Helper.runWithNetwork(new Helper.Action() {
|
final boolean setHostSuccess = Helper.runWithNetwork(() -> {
|
||||||
@Override
|
try {
|
||||||
public boolean run() {
|
nodeInfo.setHost(host);
|
||||||
try {
|
return true;
|
||||||
nodeInfo.setHost(host);
|
} catch (UnknownHostException ex) {
|
||||||
return true;
|
return false;
|
||||||
} catch (UnknownHostException ex) {
|
|
||||||
etNodeHost.setError(getString(R.string.node_host_unresolved));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!setHostSuccess) {
|
if (!setHostSuccess) {
|
||||||
@@ -450,7 +455,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private void closeDialog() {
|
private void closeDialog() {
|
||||||
if (editDialog == null) throw new IllegalStateException();
|
if (editDialog == null) throw new IllegalStateException();
|
||||||
Helper.hideKeyboardAlways(getActivity());
|
Helper.hideKeyboardAlways(requireActivity());
|
||||||
editDialog.dismiss();
|
editDialog.dismiss();
|
||||||
editDialog = null;
|
editDialog = null;
|
||||||
NodeFragment.this.editDialog = null;
|
NodeFragment.this.editDialog = null;
|
||||||
@@ -532,20 +537,10 @@ public class NodeFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onShow(final DialogInterface dialog) {
|
public void onShow(final DialogInterface dialog) {
|
||||||
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
|
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||||
testButton.setOnClickListener(new View.OnClickListener() {
|
testButton.setOnClickListener(view -> test());
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
button.setOnClickListener(view -> apply());
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -553,15 +548,13 @@ public class NodeFragment extends Fragment
|
|||||||
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etNodePass.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
||||||
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
test();
|
||||||
test();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,9 +582,11 @@ public class NodeFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
nodesAdapter.setNodes();
|
nodesAdapter.setNodes();
|
||||||
}
|
}
|
||||||
|
nodesAdapter.notifyItemChanged(nodeInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreDefaultNodes() {
|
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);
|
||||||
|
}
|
@@ -22,7 +22,6 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.nfc.NfcManager;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
@@ -33,12 +32,10 @@ import android.view.KeyEvent;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -46,10 +43,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.ShareActionProvider;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
@@ -88,7 +82,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private ImageView ivQrCode;
|
private ImageView ivQrCode;
|
||||||
private ImageView ivQrCodeFull;
|
private ImageView ivQrCodeFull;
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
private ImageButton bCopyAddress;
|
|
||||||
|
|
||||||
private Wallet wallet = null;
|
private Wallet wallet = null;
|
||||||
private boolean isMyWallet = false;
|
private boolean isMyWallet = false;
|
||||||
@@ -119,15 +112,15 @@ public class ReceiveFragment extends Fragment {
|
|||||||
tvQrCode = view.findViewById(R.id.tvQrCode);
|
tvQrCode = view.findViewById(R.id.tvQrCode);
|
||||||
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
||||||
etDummy = view.findViewById(R.id.etDummy);
|
etDummy = view.findViewById(R.id.etDummy);
|
||||||
bCopyAddress = view.findViewById(R.id.bCopyAddress);
|
|
||||||
|
|
||||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
|
|
||||||
bCopyAddress.setOnClickListener(v -> copyAddress());
|
view.findViewById(R.id.bCopyAddress).setOnClickListener(v -> copyAddress());
|
||||||
|
|
||||||
evAmount.setOnNewAmountListener(xmr -> {
|
evAmount.setOnNewAmountListener(xmr -> {
|
||||||
Timber.d("new amount = %s", xmr);
|
Timber.d("new amount = %s", xmr);
|
||||||
generateQr();
|
generateQr();
|
||||||
|
if (shareRequested && (xmr != null)) share();
|
||||||
});
|
});
|
||||||
|
|
||||||
evAmount.setOnFailedExchangeListener(() -> {
|
evAmount.setOnFailedExchangeListener(() -> {
|
||||||
@@ -192,11 +185,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
throw new IllegalStateException("no wallet info");
|
throw new IllegalStateException("no wallet info");
|
||||||
}
|
}
|
||||||
|
|
||||||
View tvNfc = view.findViewById(R.id.tvNfc);
|
|
||||||
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
|
|
||||||
if ((manager != null) && (manager.getDefaultAdapter() != null))
|
|
||||||
tvNfc.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,42 +199,40 @@ public class ReceiveFragment extends Fragment {
|
|||||||
setSharedElementEnterTransition(transform);
|
setSharedElementEnterTransition(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShareActionProvider shareActionProvider;
|
private boolean shareRequested = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.receive_menu, menu);
|
inflater.inflate(R.menu.receive_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
|
||||||
// Locate MenuItem with ShareActionProvider
|
menu.findItem(R.id.menu_item_share).setOnMenuItemClickListener(item -> {
|
||||||
MenuItem item = menu.findItem(R.id.menu_item_share);
|
if (shareRequested) return true;
|
||||||
|
shareRequested = true;
|
||||||
// Fetch and store ShareActionProvider
|
if (!qrValid) {
|
||||||
shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
|
evAmount.doExchange();
|
||||||
|
} else {
|
||||||
shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
|
share();
|
||||||
@Override
|
|
||||||
public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
|
|
||||||
saveQrCode(); // save it only if we need it
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setShareIntent() {
|
private void share() {
|
||||||
if (shareActionProvider != null) {
|
shareRequested = false;
|
||||||
if (qrValid) {
|
if (saveQrCode()) {
|
||||||
shareActionProvider.setShareIntent(getShareIntent());
|
final Intent sendIntent = getSendIntent();
|
||||||
} else {
|
if (sendIntent != null)
|
||||||
shareActionProvider.setShareIntent(null);
|
startActivity(Intent.createChooser(sendIntent, null));
|
||||||
}
|
} else {
|
||||||
|
Toast.makeText(getActivity(), getString(R.string.message_qr_failed), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveQrCode() {
|
private boolean saveQrCode() {
|
||||||
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
||||||
|
|
||||||
File cachePath = new File(getActivity().getCacheDir(), "images");
|
File cachePath = new File(requireActivity().getCacheDir(), "images");
|
||||||
if (!cachePath.exists())
|
if (!cachePath.exists())
|
||||||
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
||||||
File png = new File(cachePath, "QR.png");
|
File png = new File(cachePath, "QR.png");
|
||||||
@@ -255,33 +241,35 @@ public class ReceiveFragment extends Fragment {
|
|||||||
Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap();
|
Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap();
|
||||||
qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||||
stream.close();
|
stream.close();
|
||||||
|
return true;
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Timber.e(ex);
|
Timber.e(ex);
|
||||||
// make sure we don't share an old qr code
|
// make sure we don't share an old qr code
|
||||||
if (!png.delete()) throw new IllegalStateException("cannot delete old qr code");
|
if (!png.delete()) throw new IllegalStateException("cannot delete old qr code");
|
||||||
// if we manage to delete it, the URI points to nothing and the user gets a toast with the error
|
// if we manage to delete it, the URI points to nothing and the user gets a toast with the error
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Intent getShareIntent() {
|
private Intent getSendIntent() {
|
||||||
File imagePath = new File(getActivity().getCacheDir(), "images");
|
File imagePath = new File(requireActivity().getCacheDir(), "images");
|
||||||
File png = new File(imagePath, "QR.png");
|
File png = new File(imagePath, "QR.png");
|
||||||
Uri contentUri = FileProvider.getUriForFile(getActivity(),
|
Uri contentUri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
||||||
BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
|
||||||
if (contentUri != null) {
|
if (contentUri != null) {
|
||||||
Intent shareIntent = new Intent();
|
Intent shareIntent = new Intent();
|
||||||
shareIntent.setAction(Intent.ACTION_SEND);
|
shareIntent.setAction(Intent.ACTION_SEND);
|
||||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
|
||||||
shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri));
|
shareIntent.setTypeAndNormalize("image/png");
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
|
if (bcData != null)
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
|
||||||
return shareIntent;
|
return shareIntent;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyAddress() {
|
void copyAddress() {
|
||||||
Helper.clipBoardCopy(Objects.requireNonNull(getActivity()), getString(R.string.label_copy_address), subaddress.getAddress());
|
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_address), subaddress.getAddress());
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +279,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
if (qrValid) {
|
if (qrValid) {
|
||||||
ivQrCode.setImageBitmap(null);
|
ivQrCode.setImageBitmap(null);
|
||||||
qrValid = false;
|
qrValid = false;
|
||||||
setShareIntent();
|
|
||||||
if (isLoaded)
|
if (isLoaded)
|
||||||
tvQrCode.setVisibility(View.VISIBLE);
|
tvQrCode.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
@@ -300,7 +287,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
void setQR(Bitmap qr) {
|
void setQR(Bitmap qr) {
|
||||||
ivQrCode.setImageBitmap(qr);
|
ivQrCode.setImageBitmap(qr);
|
||||||
qrValid = true;
|
qrValid = true;
|
||||||
setShareIntent();
|
|
||||||
tvQrCode.setVisibility(View.GONE);
|
tvQrCode.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,24 +371,18 @@ public class ReceiveFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Bitmap addLogo(Bitmap qrBitmap) {
|
private Bitmap addLogo(Bitmap qrBitmap) {
|
||||||
|
// addume logo & qrcode are both square
|
||||||
Bitmap logo = getMoneroLogo();
|
Bitmap logo = getMoneroLogo();
|
||||||
int qrWidth = qrBitmap.getWidth();
|
final int qrSize = qrBitmap.getWidth();
|
||||||
int qrHeight = qrBitmap.getHeight();
|
final int logoSize = logo.getWidth();
|
||||||
int logoWidth = logo.getWidth();
|
|
||||||
int logoHeight = logo.getHeight();
|
|
||||||
|
|
||||||
Bitmap logoBitmap = Bitmap.createBitmap(qrWidth, qrHeight, Bitmap.Config.ARGB_8888);
|
Bitmap logoBitmap = Bitmap.createBitmap(qrSize, qrSize, Bitmap.Config.ARGB_8888);
|
||||||
Canvas canvas = new Canvas(logoBitmap);
|
Canvas canvas = new Canvas(logoBitmap);
|
||||||
canvas.drawBitmap(qrBitmap, 0, 0, null);
|
canvas.drawBitmap(qrBitmap, 0, 0, null);
|
||||||
canvas.save();
|
canvas.save();
|
||||||
// figure out how to scale the logo
|
final float sx = 0.2f * qrSize / logoSize;
|
||||||
float scaleSize = 1.0f;
|
canvas.scale(sx, sx, qrSize / 2f, qrSize / 2f);
|
||||||
while ((logoWidth / scaleSize) > (qrWidth / 5.) || (logoHeight / scaleSize) > (qrHeight / 5.)) {
|
canvas.drawBitmap(logo, (qrSize - logoSize) / 2f, (qrSize - logoSize) / 2f, null);
|
||||||
scaleSize *= 2;
|
|
||||||
}
|
|
||||||
float sx = 1.0f / scaleSize;
|
|
||||||
canvas.scale(sx, sx, qrWidth / 2f, qrHeight / 2f);
|
|
||||||
canvas.drawBitmap(logo, (qrWidth - logoWidth) / 2f, (qrHeight - logoHeight) / 2f, null);
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
return logoBitmap;
|
return logoBitmap;
|
||||||
}
|
}
|
||||||
@@ -411,7 +391,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
|
|
||||||
private Bitmap getMoneroLogo() {
|
private Bitmap getMoneroLogo() {
|
||||||
if (logo == null) {
|
if (logo == null) {
|
||||||
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_logo_b);
|
logo = Helper.getBitmap(getContext(), R.drawable.ic_monerujo_qr);
|
||||||
}
|
}
|
||||||
return logo;
|
return logo;
|
||||||
}
|
}
|
||||||
@@ -466,10 +446,10 @@ public class ReceiveFragment extends Fragment {
|
|||||||
.withEndAction(resetSize).start();
|
.withEndAction(resetSize).start();
|
||||||
}
|
}
|
||||||
subaddress = newSubaddress;
|
subaddress = newSubaddress;
|
||||||
final Context context = getContext();
|
final Context context = requireContext();
|
||||||
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
||||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
subaddress.getDisplayLabel(), subaddress.getAddress()));
|
subaddress.getDisplayLabel(), subaddress.getAddress()));
|
||||||
tvAddress.setText(label);
|
tvAddress.setText(label);
|
||||||
generateQr();
|
generateQr();
|
||||||
|
@@ -19,12 +19,14 @@ package com.m2049r.xmrwallet;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
private ZXingScannerView mScannerView;
|
private ZXingScannerView mScannerView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Timber.d("onCreateView");
|
Timber.d("onCreateView");
|
||||||
mScannerView = new ZXingScannerView(getActivity());
|
mScannerView = new ZXingScannerView(getActivity());
|
||||||
return mScannerView;
|
return mScannerView;
|
||||||
@@ -84,7 +86,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof OnScannedListener) {
|
if (context instanceof OnScannedListener) {
|
||||||
this.onScannedListener = (OnScannedListener) context;
|
this.onScannedListener = (OnScannedListener) context;
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import static android.view.WindowManager.LayoutParams;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -29,8 +31,6 @@ import com.m2049r.xmrwallet.util.LocaleHelper;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static android.view.WindowManager.LayoutParams;
|
|
||||||
|
|
||||||
public abstract class SecureActivity extends AppCompatActivity {
|
public abstract class SecureActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@@ -66,11 +66,7 @@ public abstract class SecureActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
Locale locale = LocaleHelper.getPreferredLocale(this);
|
Locale locale = LocaleHelper.getPreferredLocale(this);
|
||||||
if (locale != null) {
|
if (locale != null) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
config.setLocale(locale);
|
||||||
config.setLocale(locale);
|
|
||||||
} else {
|
|
||||||
config.locale = locale;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return config;
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -30,7 +30,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.transition.MaterialElevationScale;
|
|
||||||
import com.m2049r.xmrwallet.data.Subaddress;
|
import com.m2049r.xmrwallet.data.Subaddress;
|
||||||
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
|
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
|
||||||
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
@@ -48,7 +47,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class SubaddressFragment extends Fragment implements SubaddressInfoAdapter.OnInteractionListener,
|
public class SubaddressFragment extends Fragment implements SubaddressInfoAdapter.OnInteractionListener,
|
||||||
View.OnClickListener {
|
View.OnClickListener, OnBlockUpdateListener {
|
||||||
static public final String KEY_MODE = "mode";
|
static public final String KEY_MODE = "mode";
|
||||||
static public final String MODE_MANAGER = "manager";
|
static public final String MODE_MANAGER = "manager";
|
||||||
|
|
||||||
@@ -67,6 +66,8 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
void setToolbarButton(int type);
|
void setToolbarButton(int type);
|
||||||
|
|
||||||
void showSubaddress(View view, final int subaddressIndex);
|
void showSubaddress(View view, final int subaddressIndex);
|
||||||
|
|
||||||
|
void saveWallet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ProgressListener {
|
public interface ProgressListener {
|
||||||
@@ -115,14 +116,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
|
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
|
||||||
|
|
||||||
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
|
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
|
||||||
|
|
||||||
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
|
|
||||||
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
setExitTransition(exitTransition);
|
|
||||||
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
|
|
||||||
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
setReenterTransition(reenterTransition);
|
|
||||||
|
|
||||||
view.findViewById(R.id.fab).setOnClickListener(this);
|
view.findViewById(R.id.fab).setOnClickListener(this);
|
||||||
|
|
||||||
if (managerMode) {
|
if (managerMode) {
|
||||||
@@ -152,11 +145,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
postponeEnterTransition();
|
|
||||||
view.getViewTreeObserver().addOnPreDrawListener(() -> {
|
|
||||||
startPostponedEnterTransition();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadList() {
|
public void loadList() {
|
||||||
@@ -169,6 +157,11 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
adapter.setInfos(list);
|
adapter.setInfos(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlockUpdate(Wallet wallet) {
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int id = v.getId();
|
int id = v.getId();
|
||||||
@@ -202,7 +195,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
if ((wallet.getDeviceType() == Wallet.Device.Device_Ledger) && (progressCallback != null)) {
|
if ((wallet.getDeviceType() == Wallet.Device.Ledger) && (progressCallback != null)) {
|
||||||
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
||||||
dialogOpened = true;
|
dialogOpened = true;
|
||||||
}
|
}
|
||||||
@@ -212,7 +205,9 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
if (params.length != 0) return false;
|
if (params.length != 0) return false;
|
||||||
wallet.getNewSubaddress();
|
wallet.getNewSubaddress();
|
||||||
wallet.store();
|
if (activityCallback != null) {
|
||||||
|
activityCallback.saveWallet();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +226,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
|
|
||||||
// Callbacks from SubaddressInfoAdapter
|
// Callbacks from SubaddressInfoAdapter
|
||||||
@Override
|
@Override
|
||||||
public void onInteraction(final View view, final Subaddress subaddress) {
|
public void onInteraction(final View view, @NonNull final Subaddress subaddress) {
|
||||||
if (managerMode)
|
if (managerMode)
|
||||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
else
|
else
|
||||||
@@ -243,4 +238,5 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,15 +29,15 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.transition.Transition;
|
||||||
|
import androidx.transition.TransitionInflater;
|
||||||
|
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
import com.google.android.material.transition.MaterialContainerTransform;
|
|
||||||
import com.m2049r.xmrwallet.data.Subaddress;
|
import com.m2049r.xmrwallet.data.Subaddress;
|
||||||
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,14 +45,13 @@ import java.util.List;
|
|||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
// TODO: live update - i.e. use onRefreshed() somehow
|
public class SubaddressInfoFragment extends Fragment
|
||||||
public class SubaddressInfoFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener {
|
implements TransactionInfoAdapter.Listener, OnBlockUpdateListener {
|
||||||
private TransactionInfoAdapter adapter;
|
private TransactionInfoAdapter adapter;
|
||||||
|
|
||||||
private Subaddress subaddress;
|
private Subaddress subaddress;
|
||||||
|
|
||||||
private TextInputLayout etName;
|
private TextInputLayout etName;
|
||||||
private TextView tvAddress;
|
|
||||||
private TextView tvTxLabel;
|
private TextView tvTxLabel;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +60,6 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
||||||
|
|
||||||
etName = view.findViewById(R.id.etName);
|
etName = view.findViewById(R.id.etName);
|
||||||
tvAddress = view.findViewById(R.id.tvAddress);
|
|
||||||
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
||||||
|
|
||||||
final RecyclerView list = view.findViewById(R.id.list);
|
final RecyclerView list = view.findViewById(R.id.list);
|
||||||
@@ -71,12 +69,14 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
final Wallet wallet = activityCallback.getWallet();
|
final Wallet wallet = activityCallback.getWallet();
|
||||||
|
|
||||||
Bundle b = getArguments();
|
Bundle b = getArguments();
|
||||||
|
assert b != null;
|
||||||
final int subaddressIndex = b.getInt("subaddressIndex");
|
final int subaddressIndex = b.getInt("subaddressIndex");
|
||||||
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
||||||
|
|
||||||
etName.getEditText().setText(subaddress.getDisplayLabel());
|
etName.getEditText().setText(subaddress.getDisplayLabel());
|
||||||
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
|
final TextView tvAddress = view.findViewById(R.id.tvAddress);
|
||||||
subaddress.getAddressIndex(), subaddress.getSquashedAddress()));
|
tvAddress.setText(requireContext().getString(R.string.subbaddress_info_subtitle,
|
||||||
|
subaddress.getAddressIndex(), subaddress.getAddress()));
|
||||||
|
|
||||||
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
if (!hasFocus) {
|
if (!hasFocus) {
|
||||||
@@ -102,10 +102,8 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
Transition transform = TransitionInflater.from(requireContext())
|
||||||
transform.setDrawingViewId(R.id.fragment_container);
|
.inflateTransition(R.transition.details);
|
||||||
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
|
|
||||||
setSharedElementEnterTransition(transform);
|
setSharedElementEnterTransition(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +121,11 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
tvTxLabel.setText(R.string.subaddress_tx_label);
|
tvTxLabel.setText(R.string.subaddress_tx_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlockUpdate(Wallet wallet) {
|
||||||
|
onRefreshed(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
// Callbacks from TransactionInfoAdapter
|
// Callbacks from TransactionInfoAdapter
|
||||||
@Override
|
@Override
|
||||||
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||||
@@ -142,6 +145,8 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
void setTitle(String title, String subtitle);
|
void setTitle(String title, String subtitle);
|
||||||
|
|
||||||
void setSubtitle(String subtitle);
|
void setSubtitle(String subtitle);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -167,4 +172,9 @@ public class SubaddressInfoFragment extends Fragment implements TransactionInfoA
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDaemonHeight() {
|
||||||
|
return activityCallback.getDaemonHeight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
@@ -34,17 +33,19 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.transition.Transition;
|
||||||
|
import androidx.transition.TransitionInflater;
|
||||||
|
|
||||||
import com.google.android.material.transition.MaterialContainerTransform;
|
|
||||||
import com.google.android.material.transition.MaterialElevationScale;
|
|
||||||
import com.m2049r.xmrwallet.data.Subaddress;
|
import com.m2049r.xmrwallet.data.Subaddress;
|
||||||
import com.m2049r.xmrwallet.data.UserNotes;
|
import com.m2049r.xmrwallet.data.UserNotes;
|
||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.model.Transfer;
|
import com.m2049r.xmrwallet.model.Transfer;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftService;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.api.ShiftApi;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
@@ -62,6 +63,7 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
static public final String ARG_INFO = "info";
|
static public final String ARG_INFO = "info";
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
||||||
|
|
||||||
public TxFragment() {
|
public TxFragment() {
|
||||||
@@ -80,10 +82,14 @@ public class TxFragment extends Fragment {
|
|||||||
private TextView tvTxPaymentId;
|
private TextView tvTxPaymentId;
|
||||||
private TextView tvTxBlockheight;
|
private TextView tvTxBlockheight;
|
||||||
private TextView tvTxAmount;
|
private TextView tvTxAmount;
|
||||||
|
private TextView tvTxPocketChangeAmount;
|
||||||
private TextView tvTxFee;
|
private TextView tvTxFee;
|
||||||
private TextView tvTxTransfers;
|
private TextView tvTxTransfers;
|
||||||
private TextView etTxNotes;
|
private TextView etTxNotes;
|
||||||
|
|
||||||
|
private View llWarning;
|
||||||
|
private TextView tvWarning;
|
||||||
|
|
||||||
// XMRTO stuff
|
// XMRTO stuff
|
||||||
private View cvXmrTo;
|
private View cvXmrTo;
|
||||||
private TextView tvTxXmrToKey;
|
private TextView tvTxXmrToKey;
|
||||||
@@ -97,19 +103,13 @@ public class TxFragment extends Fragment {
|
|||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
|
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
|
||||||
|
|
||||||
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
|
|
||||||
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
setExitTransition(exitTransition);
|
|
||||||
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
|
|
||||||
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
setReenterTransition(reenterTransition);
|
|
||||||
|
|
||||||
cvXmrTo = view.findViewById(R.id.cvXmrTo);
|
cvXmrTo = view.findViewById(R.id.cvXmrTo);
|
||||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||||
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
||||||
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
||||||
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||||
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
||||||
|
|
||||||
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
||||||
|
|
||||||
tvAccount = view.findViewById(R.id.tvAccount);
|
tvAccount = view.findViewById(R.id.tvAccount);
|
||||||
@@ -121,25 +121,30 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
||||||
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
|
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
|
||||||
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
||||||
|
tvTxPocketChangeAmount = view.findViewById(R.id.tvTxPocketChangeAmount);
|
||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
||||||
etTxNotes = view.findViewById(R.id.etTxNotes);
|
etTxNotes = view.findViewById(R.id.etTxNotes);
|
||||||
|
|
||||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
|
||||||
|
llWarning = view.findViewById(R.id.llWarning);
|
||||||
|
tvWarning = view.findViewById(R.id.tvWarning);
|
||||||
|
|
||||||
tvTxXmrToKey.setOnClickListener(v -> {
|
tvTxXmrToKey.setOnClickListener(v -> {
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
info = getArguments().getParcelable(ARG_INFO);
|
final Bundle args = getArguments();
|
||||||
|
assert args != null;
|
||||||
|
info = args.getParcelable(ARG_INFO);
|
||||||
show();
|
show();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shareTxInfo() {
|
void shareTxInfo() {
|
||||||
if (this.info == null) return;
|
if (this.info == null) return;
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
||||||
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
||||||
@@ -217,11 +222,11 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
private void showSubaddressLabel() {
|
private void showSubaddressLabel() {
|
||||||
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
||||||
final Context ctx = getContext();
|
final Context ctx = requireContext();
|
||||||
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
||||||
info.accountIndex, info.addressIndex,
|
info.accountIndex, info.addressIndex,
|
||||||
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
subaddress.getDisplayLabel()));
|
subaddress.getDisplayLabel()));
|
||||||
tvAccount.setText(label);
|
tvAccount.setText(label);
|
||||||
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
|
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
|
||||||
@@ -252,8 +257,10 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||||
|
|
||||||
long realAmount = info.amount;
|
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
|
||||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
|
final long pcAmount = info.getPocketChangeAmount();
|
||||||
|
tvTxPocketChangeAmount.setVisibility(pcAmount > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
tvTxPocketChangeAmount.setText(getString(R.string.pocketchange_tx_detail, Wallet.getDisplayAmount(pcAmount)));
|
||||||
|
|
||||||
if ((info.fee > 0)) {
|
if ((info.fee > 0)) {
|
||||||
String fee = Wallet.getDisplayAmount(info.fee);
|
String fee = Wallet.getDisplayAmount(info.fee);
|
||||||
@@ -263,16 +270,17 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxFee.setVisibility(View.GONE);
|
tvTxFee.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Context ctx = requireContext();
|
||||||
if (info.isFailed) {
|
if (info.isFailed) {
|
||||||
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
||||||
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_failed));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.isPending) {
|
} else if (info.isPending) {
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_pending));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_plus));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor));
|
||||||
} else {
|
} else {
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.negativeColor));
|
||||||
}
|
}
|
||||||
Set<String> destinations = new HashSet<>();
|
Set<String> destinations = new HashSet<>();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -307,37 +315,49 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxTransfers.setText(sb.toString());
|
tvTxTransfers.setText(sb.toString());
|
||||||
tvDestination.setText(dstSb.toString());
|
tvDestination.setText(dstSb.toString());
|
||||||
showBtcInfo();
|
showBtcInfo();
|
||||||
|
|
||||||
|
showLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLock() {
|
||||||
|
llWarning.setVisibility(View.GONE);
|
||||||
|
if (info.unlockTime == 0) return;
|
||||||
|
final long blockheight = activityCallback.getDaemonHeight();
|
||||||
|
final long blocks = info.unlockTime - blockheight;
|
||||||
|
final double unlockDays = blocks / (30. * 24);
|
||||||
|
if (unlockDays > 0) {
|
||||||
|
llWarning.setVisibility(View.VISIBLE);
|
||||||
|
tvWarning.setText(getString(R.string.tx_locked, info.unlockTime, blocks, unlockDays));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
void showBtcInfo() {
|
void showBtcInfo() {
|
||||||
if (userNotes.xmrtoKey != null) {
|
if (userNotes.xmrtoKey != null) {
|
||||||
cvXmrTo.setVisibility(View.VISIBLE);
|
cvXmrTo.setVisibility(View.VISIBLE);
|
||||||
String key = userNotes.xmrtoKey;
|
|
||||||
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
|
ShiftService service = ShiftService.findWithTag(userNotes.xmrtoTag);
|
||||||
key = "xmrto-" + key;
|
tvXmrToKeyLabel.setText(getString(R.string.label_send_btc_xmrto_key_lb, service.getLabel()));
|
||||||
}
|
if (service.getIconId() == 0)
|
||||||
tvTxXmrToKey.setText(key);
|
tvXmrToLogo.setVisibility(View.GONE);
|
||||||
|
else
|
||||||
|
tvXmrToLogo.setImageResource(service.getLogoId());
|
||||||
|
|
||||||
|
tvTxXmrToKey.setText(userNotes.xmrtoKey);
|
||||||
|
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||||
switch (userNotes.xmrtoTag) {
|
|
||||||
case "xmrto":
|
ShiftApi shiftApi = service.getShiftApi();
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
if (shiftApi != null) {
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
tvXmrToSupport.setText(getString(R.string.label_send_btc_xmrto_info, service.getLabel()));
|
||||||
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
|
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
break;
|
tvXmrToSupport.setOnClickListener(v -> {
|
||||||
case "side": // defaults in layout - just add underline
|
startActivity(new Intent(Intent.ACTION_VIEW, shiftApi.getQueryOrderUri(userNotes.xmrtoKey)));
|
||||||
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
});
|
||||||
tvXmrToSupport.setOnClickListener(v -> {
|
} else {
|
||||||
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
|
||||||
tvXmrToLogo.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cvXmrTo.setVisibility(View.GONE);
|
cvXmrTo.setVisibility(View.GONE);
|
||||||
@@ -348,15 +368,13 @@ public class TxFragment extends Fragment {
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
Transition transform = TransitionInflater.from(requireContext())
|
||||||
transform.setDrawingViewId(R.id.fragment_container);
|
.inflateTransition(R.transition.details);
|
||||||
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
|
||||||
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
|
|
||||||
setSharedElementEnterTransition(transform);
|
setSharedElementEnterTransition(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.tx_info_menu, menu);
|
inflater.inflate(R.menu.tx_info_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
@@ -380,10 +398,11 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
void showSubaddress(View view, final int subaddressIndex);
|
void showSubaddress(View view, final int subaddressIndex);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof TxFragment.Listener) {
|
if (context instanceof TxFragment.Listener) {
|
||||||
this.activityCallback = (TxFragment.Listener) context;
|
this.activityCallback = (TxFragment.Listener) context;
|
||||||
|
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