mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
131 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
@@ -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,15 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
|
||||||
buildToolsVersion '29.0.3'
|
|
||||||
ndkVersion '17.2.4988734'
|
ndkVersion '17.2.4988734'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
|
buildToolsVersion = '35.0.0'
|
||||||
|
compileSdk 35
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 35
|
||||||
versionCode 1005
|
versionCode 4105
|
||||||
versionName "2.0.5 'Puginarug'"
|
versionName "4.1.5 'Exolix'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -24,7 +24,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions 'type', 'net'
|
flavorDimensions = ['type', 'net']
|
||||||
productFlavors {
|
productFlavors {
|
||||||
mainnet {
|
mainnet {
|
||||||
dimension 'net'
|
dimension 'net'
|
||||||
@@ -58,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") + "\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,44 +111,61 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
namespace 'com.m2049r.xmrwallet'
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
all {
|
||||||
|
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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:1.8.22"))
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'androidx.core:core:1.13.1'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||||
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.1.4'
|
||||||
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
|
|
||||||
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
|
|
||||||
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 "io.github.rburgst:okhttp-digest:2.5"
|
implementation "io.github.rburgst:okhttp-digest:3.1.0"
|
||||||
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.8.2'
|
||||||
|
|
||||||
implementation 'dnsjava:dnsjava:2.1.9'
|
implementation 'dnsjava:dnsjava:3.5.3'
|
||||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
implementation 'org.slf4j:slf4j-nop:2.0.11'
|
||||||
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:20231013'
|
||||||
|
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||||
|
|
||||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||||
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,31 +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.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.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:preserveLegacyExternalStorage="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>
|
||||||
@@ -34,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" />
|
||||||
@@ -75,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"
|
||||||
@@ -87,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() {
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,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;
|
||||||
}
|
}
|
||||||
@@ -460,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;
|
||||||
@@ -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() {
|
||||||
@@ -207,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;
|
||||||
}
|
}
|
||||||
@@ -217,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,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
|
||||||
@@ -248,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;
|
||||||
@@ -46,13 +46,12 @@ import java.util.List;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class SubaddressInfoFragment extends Fragment
|
public class SubaddressInfoFragment extends Fragment
|
||||||
implements TransactionInfoAdapter.OnInteractionListener, OnBlockUpdateListener {
|
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
|
|||||||
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
|
|||||||
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
|
|||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +145,8 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
void setTitle(String title, String subtitle);
|
void setTitle(String title, String subtitle);
|
||||||
|
|
||||||
void setSubtitle(String subtitle);
|
void setSubtitle(String subtitle);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -172,4 +172,9 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
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
@@ -43,7 +43,6 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
|
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
|
||||||
import com.google.android.material.transition.MaterialElevationScale;
|
|
||||||
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;
|
||||||
@@ -52,18 +51,19 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
|||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.StickyFiatHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class WalletFragment extends Fragment
|
public class WalletFragment extends Fragment
|
||||||
implements TransactionInfoAdapter.OnInteractionListener {
|
implements TransactionInfoAdapter.Listener {
|
||||||
private TransactionInfoAdapter adapter;
|
private TransactionInfoAdapter adapter;
|
||||||
private final NumberFormat formatter = NumberFormat.getInstance();
|
private final NumberFormat formatter = NumberFormat.getInstance();
|
||||||
|
|
||||||
@@ -112,15 +112,16 @@ public class WalletFragment extends Fragment
|
|||||||
llBalance = view.findViewById(R.id.llBalance);
|
llBalance = view.findViewById(R.id.llBalance);
|
||||||
flExchange = view.findViewById(R.id.flExchange);
|
flExchange = view.findViewById(R.id.flExchange);
|
||||||
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
||||||
setColorFilter(getResources().getColor(R.color.progress_circle),
|
setColorFilter(
|
||||||
|
ThemeHelper.getThemedColor(requireContext(), com.google.android.material.R.attr.colorPrimaryVariant),
|
||||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
|
|
||||||
tvProgress = view.findViewById(R.id.tvProgress);
|
tvProgress = view.findViewById(R.id.tvProgress);
|
||||||
pbProgress = view.findViewById(R.id.pbProgress);
|
pbProgress = view.findViewById(R.id.pbProgress);
|
||||||
tvBalance = view.findViewById(R.id.tvBalance);
|
tvBalance = view.findViewById(R.id.tvBalance);
|
||||||
showBalance(Helper.getDisplayAmount(0));
|
showBalance();
|
||||||
tvUnconfirmedAmount = view.findViewById(R.id.tvUnconfirmedAmount);
|
tvUnconfirmedAmount = view.findViewById(R.id.tvUnconfirmedAmount);
|
||||||
showUnconfirmed(0);
|
showUnconfirmed();
|
||||||
ivSynced = view.findViewById(R.id.ivSynced);
|
ivSynced = view.findViewById(R.id.ivSynced);
|
||||||
|
|
||||||
sCurrency = view.findViewById(R.id.sCurrency);
|
sCurrency = view.findViewById(R.id.sCurrency);
|
||||||
@@ -128,9 +129,10 @@ public class WalletFragment extends Fragment
|
|||||||
currencies.add(Helper.BASE_CRYPTO);
|
currencies.add(Helper.BASE_CRYPTO);
|
||||||
if (Helper.SHOW_EXCHANGERATES)
|
if (Helper.SHOW_EXCHANGERATES)
|
||||||
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
|
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
|
||||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(Objects.requireNonNull(getContext()), R.layout.item_spinner_balance, currencies);
|
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_balance, currencies);
|
||||||
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
sCurrency.setAdapter(spinnerAdapter);
|
sCurrency.setAdapter(spinnerAdapter);
|
||||||
|
StickyFiatHelper.setPreferredCurrencyPosition(sCurrency);
|
||||||
|
|
||||||
bSend = view.findViewById(R.id.bSend);
|
bSend = view.findViewById(R.id.bSend);
|
||||||
bReceive = view.findViewById(R.id.bReceive);
|
bReceive = view.findViewById(R.id.bReceive);
|
||||||
@@ -182,6 +184,7 @@ public class WalletFragment extends Fragment
|
|||||||
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
|
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
|
||||||
|
StickyFiatHelper.setPreferredFiatSymbol(requireContext(), (String) sCurrency.getSelectedItem());
|
||||||
refreshBalance();
|
refreshBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,14 +206,20 @@ public class WalletFragment extends Fragment
|
|||||||
@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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void showBalance(String balance) {
|
String amountToString(double amount) {
|
||||||
|
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||||
|
double amountB = amount * balanceRate;
|
||||||
|
return Helper.getFormattedAmount(amountB, false);
|
||||||
|
} else { // XMR
|
||||||
|
return Helper.getFormattedAmount(amount, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showBalance() {
|
||||||
|
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
|
String balance = amountToString(amountA);
|
||||||
tvBalance.setText(balance);
|
tvBalance.setText(balance);
|
||||||
final boolean streetMode = activityCallback.isStreetMode();
|
final boolean streetMode = activityCallback.isStreetMode();
|
||||||
if (!streetMode) {
|
if (!streetMode) {
|
||||||
@@ -223,27 +232,23 @@ public class WalletFragment extends Fragment
|
|||||||
setStreetModeBackground(streetMode);
|
setStreetModeBackground(streetMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showUnconfirmed(double unconfirmedAmount) {
|
void showUnconfirmed() {
|
||||||
if (!activityCallback.isStreetMode()) {
|
double unconfirmedAmount = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
||||||
String unconfirmed = Helper.getFormattedAmount(unconfirmedAmount, true);
|
if (activityCallback.isStreetMode() || unconfirmedAmount == 0) {
|
||||||
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed));
|
|
||||||
} else {
|
|
||||||
tvUnconfirmedAmount.setText(null);
|
tvUnconfirmedAmount.setText(null);
|
||||||
|
tvUnconfirmedAmount.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
String unconfirmed = amountToString(unconfirmedAmount);
|
||||||
|
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed, balanceCurrency));
|
||||||
|
tvUnconfirmedAmount.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateBalance() {
|
void updateBalance() {
|
||||||
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
showBalance();
|
||||||
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
showUnconfirmed();
|
||||||
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
|
|
||||||
double amountB = amountA * balanceRate;
|
|
||||||
displayB = Helper.getFormattedAmount(amountB, false);
|
|
||||||
} else { // XMR
|
|
||||||
displayB = Helper.getFormattedAmount(amountA, true);
|
|
||||||
}
|
|
||||||
showBalance(displayB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String balanceCurrency = Helper.BASE_CRYPTO;
|
String balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
@@ -252,11 +257,11 @@ public class WalletFragment extends Fragment
|
|||||||
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
|
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
|
||||||
|
|
||||||
void refreshBalance() {
|
void refreshBalance() {
|
||||||
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
|
||||||
showUnconfirmed(unconfirmedXmr);
|
|
||||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
balanceRate = 1.0;
|
||||||
|
showBalance();
|
||||||
|
showUnconfirmed();
|
||||||
} else { // not XMR
|
} else { // not XMR
|
||||||
String currency = (String) sCurrency.getSelectedItem();
|
String currency = (String) sCurrency.getSelectedItem();
|
||||||
Timber.d(currency);
|
Timber.d(currency);
|
||||||
@@ -301,8 +306,7 @@ public class WalletFragment extends Fragment
|
|||||||
|
|
||||||
public void exchangeFailed() {
|
public void exchangeFailed() {
|
||||||
sCurrency.setSelection(0, true); // default to XMR
|
sCurrency.setSelection(0, true); // default to XMR
|
||||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
showBalance();
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
|
||||||
hideExchanging();
|
hideExchanging();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,13 +318,7 @@ public class WalletFragment extends Fragment
|
|||||||
balanceCurrency = Helper.BASE_CRYPTO;
|
balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
balanceRate = 1.0;
|
balanceRate = 1.0;
|
||||||
} else {
|
} else {
|
||||||
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
StickyFiatHelper.setCurrencyPosition(sCurrency, exchangeRate.getQuoteCurrency());
|
||||||
if (spinnerPosition < 0) { // requested currency not in list
|
|
||||||
Timber.e("Requested currency not in list %s", exchangeRate.getQuoteCurrency());
|
|
||||||
sCurrency.setSelection(0, true);
|
|
||||||
} else {
|
|
||||||
sCurrency.setSelection(spinnerPosition, true);
|
|
||||||
}
|
|
||||||
balanceCurrency = exchangeRate.getQuoteCurrency();
|
balanceCurrency = exchangeRate.getQuoteCurrency();
|
||||||
balanceRate = exchangeRate.getRate();
|
balanceRate = exchangeRate.getRate();
|
||||||
}
|
}
|
||||||
@@ -330,29 +328,24 @@ public class WalletFragment extends Fragment
|
|||||||
// 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) {
|
||||||
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);
|
|
||||||
|
|
||||||
activityCallback.onTxDetailsRequest(view, infoItem);
|
activityCallback.onTxDetailsRequest(view, infoItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from activity
|
|
||||||
|
|
||||||
|
|
||||||
// if account index has changed scroll to top?
|
// if account index has changed scroll to top?
|
||||||
private int accountIndex = 0;
|
private int accountIndex = 0;
|
||||||
|
|
||||||
public void onRefreshed(final Wallet wallet, final boolean full) {
|
public void onRefreshed(final Wallet wallet, boolean full) {
|
||||||
Timber.d("onRefreshed(%b)", full);
|
Timber.d("onRefreshed(%b)", full);
|
||||||
|
|
||||||
|
if (adapter.needsTransactionUpdateOnNewBlock()) {
|
||||||
|
wallet.refreshHistory();
|
||||||
|
full = true;
|
||||||
|
}
|
||||||
if (full) {
|
if (full) {
|
||||||
List<TransactionInfo> list = new ArrayList<>();
|
List<TransactionInfo> list = new ArrayList<>();
|
||||||
final long streetHeight = activityCallback.getStreetModeHeight();
|
final long streetHeight = activityCallback.getStreetModeHeight();
|
||||||
Timber.d("StreetHeight=%d", streetHeight);
|
Timber.d("StreetHeight=%d", streetHeight);
|
||||||
|
wallet.refreshHistory();
|
||||||
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
||||||
Timber.d("TxHeight=%d, Label=%s", info.blockheight, info.subaddressLabel);
|
Timber.d("TxHeight=%d, Label=%s", info.blockheight, info.subaddressLabel);
|
||||||
if ((info.isPending || (info.blockheight >= streetHeight))
|
if ((info.isPending || (info.blockheight >= streetHeight))
|
||||||
@@ -451,12 +444,13 @@ public class WalletFragment extends Fragment
|
|||||||
String sync;
|
String sync;
|
||||||
if (!activityCallback.hasBoundService())
|
if (!activityCallback.hasBoundService())
|
||||||
throw new IllegalStateException("WalletService not bound.");
|
throw new IllegalStateException("WalletService not bound.");
|
||||||
|
ivSynced.setVisibility(View.GONE);
|
||||||
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
||||||
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||||
if (!wallet.isSynchronized()) {
|
if (!wallet.isSynchronized()) {
|
||||||
long daemonHeight = activityCallback.getDaemonHeight();
|
final long daemonHeight = getDaemonHeight();
|
||||||
long walletHeight = wallet.getBlockChainHeight();
|
final long walletHeight = wallet.getBlockChainHeight();
|
||||||
long n = daemonHeight - walletHeight;
|
final long n = daemonHeight - walletHeight;
|
||||||
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
|
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
|
||||||
if (firstBlock == 0) {
|
if (firstBlock == 0) {
|
||||||
firstBlock = walletHeight;
|
firstBlock = walletHeight;
|
||||||
@@ -464,7 +458,6 @@ public class WalletFragment extends Fragment
|
|||||||
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
|
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
|
||||||
if (x == 0) x = 101; // indeterminate
|
if (x == 0) x = 101; // indeterminate
|
||||||
setProgress(x);
|
setProgress(x);
|
||||||
ivSynced.setVisibility(View.GONE);
|
|
||||||
} else {
|
} else {
|
||||||
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
||||||
ivSynced.setVisibility(View.VISIBLE);
|
ivSynced.setVisibility(View.VISIBLE);
|
||||||
@@ -530,8 +523,6 @@ public class WalletFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
setExitTransition(null);
|
|
||||||
setReenterTransition(null);
|
|
||||||
Timber.d("onResume()");
|
Timber.d("onResume()");
|
||||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
|
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
|
||||||
@@ -561,9 +552,14 @@ public class WalletFragment extends Fragment
|
|||||||
//TODO figure out why gunther disappears on return from send although he is still set
|
//TODO figure out why gunther disappears on return from send although he is still set
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (streetGunther == null)
|
if (streetGunther == null)
|
||||||
streetGunther = ContextCompat.getDrawable(Objects.requireNonNull(getContext()), R.drawable.ic_gunther_streetmode);
|
streetGunther = ContextCompat.getDrawable(requireContext(), R.drawable.ic_gunther_streetmode);
|
||||||
ivStreetGunther.setImageDrawable(streetGunther);
|
ivStreetGunther.setImageDrawable(streetGunther);
|
||||||
} else
|
} else
|
||||||
ivStreetGunther.setImageDrawable(null);
|
ivStreetGunther.setImageDrawable(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDaemonHeight() {
|
||||||
|
return activityCallback.getDaemonHeight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,22 +21,27 @@ import android.content.Context;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.BuildConfig;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class XmrWalletApplication extends Application {
|
public class XmrWalletApplication extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(new Timber.DebugTree());
|
Timber.plant(new Timber.DebugTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
NightmodeHelper.setPreferredNightmode(this);
|
NightmodeHelper.setPreferredNightmode(this);
|
||||||
|
|
||||||
|
NetCipherHelper.createInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,7 +50,7 @@ public class XmrWalletApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(Configuration configuration) {
|
public void onConfigurationChanged(@NonNull Configuration configuration) {
|
||||||
super.onConfigurationChanged(configuration);
|
super.onConfigurationChanged(configuration);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
LocaleHelper.updateSystemDefaultLocale(configuration.getLocales().get(0));
|
LocaleHelper.updateSystemDefaultLocale(configuration.getLocales().get(0));
|
||||||
|
@@ -24,43 +24,38 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
public class BarcodeData {
|
public class BarcodeData {
|
||||||
|
|
||||||
public enum Security {
|
public enum Security {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
OA_NO_DNSSEC,
|
OA_NO_DNSSEC,
|
||||||
OA_DNSSEC
|
OA_DNSSEC
|
||||||
}
|
}
|
||||||
|
|
||||||
final public Crypto asset;
|
final private Set<Crypto> possibleAssets = new HashSet<>();
|
||||||
final public List<Crypto> ambiguousAssets;
|
private String address = null;
|
||||||
final public String address;
|
private String addressName = null;
|
||||||
final public String addressName;
|
private String amount = null;
|
||||||
final public String amount;
|
private String description = null;
|
||||||
final public String description;
|
private Security security = null;
|
||||||
final public Security security;
|
|
||||||
|
|
||||||
public BarcodeData(List<Crypto> assets, String address) {
|
public BarcodeData(List<Crypto> assets, String address) {
|
||||||
if (assets.isEmpty())
|
if (assets.isEmpty())
|
||||||
throw new IllegalArgumentException("no assets specified");
|
throw new IllegalArgumentException("no assets specified");
|
||||||
this.addressName = null;
|
security = Security.NORMAL;
|
||||||
this.description = null;
|
|
||||||
this.amount = null;
|
|
||||||
this.security = Security.NORMAL;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
if (assets.size() == 1) {
|
possibleAssets.addAll(assets);
|
||||||
this.asset = assets.get(0);
|
|
||||||
this.ambiguousAssets = null;
|
|
||||||
} else {
|
|
||||||
this.asset = null;
|
|
||||||
this.ambiguousAssets = assets;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
||||||
@@ -68,8 +63,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
||||||
this.ambiguousAssets = null;
|
possibleAssets.add(asset);
|
||||||
this.asset = asset;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.addressName = addressName;
|
this.addressName = addressName;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@@ -82,7 +76,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getUriString() {
|
public String getUriString() {
|
||||||
if (asset != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
if (getAsset() != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(Crypto.XMR.getUriScheme())
|
sb.append(Crypto.XMR.getUriScheme())
|
||||||
.append(':')
|
.append(':')
|
||||||
@@ -227,6 +221,20 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAmbiguous() {
|
public boolean isAmbiguous() {
|
||||||
return ambiguousAssets != null;
|
return possibleAssets.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crypto getAsset() {
|
||||||
|
if (possibleAssets.size() == 1) {
|
||||||
|
return possibleAssets.iterator().next();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if we still have possible assets
|
||||||
|
public boolean filter(Set<Crypto> assets) {
|
||||||
|
possibleAssets.retainAll(assets);
|
||||||
|
return !possibleAssets.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user