mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 17:28:42 +02:00
Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cc7cdb383c | ||
![]() |
ac1ea05ef6 | ||
![]() |
1ddd4f30b9 | ||
![]() |
1dc081834f | ||
![]() |
ce084927e1 | ||
![]() |
3610781f43 | ||
![]() |
ef3ddbac71 | ||
![]() |
0512af1496 | ||
![]() |
bd2c49669a | ||
![]() |
ac7831d0f9 | ||
![]() |
0f0b9a38c7 | ||
![]() |
807db19603 | ||
![]() |
c956f38899 | ||
![]() |
db68f517d3 | ||
![]() |
d4b293af80 | ||
![]() |
f7bbfc2fac | ||
![]() |
e08964749e | ||
![]() |
a05fa9d177 | ||
![]() |
7fe2fbe37d | ||
![]() |
40e30fed08 | ||
![]() |
320c7865ff | ||
![]() |
5e8cf8010e | ||
![]() |
e671fa19e0 | ||
![]() |
20d5b9a100 | ||
![]() |
5d489a634b | ||
![]() |
59b6f484fd | ||
![]() |
ecaa49d67d | ||
![]() |
d2dc53599e | ||
![]() |
4d8b26f97f | ||
![]() |
581c76e7be | ||
![]() |
6f66862870 | ||
![]() |
dd92f7bb36 | ||
![]() |
46808d306b | ||
![]() |
20503d2cbd | ||
![]() |
604691ca7e | ||
![]() |
1b626ba2b0 | ||
![]() |
0ed7bdfcee | ||
![]() |
524c3dd79f | ||
![]() |
197dffeae1 | ||
![]() |
cdb29bbc2e | ||
![]() |
7b96baeca7 | ||
![]() |
0712efec78 | ||
![]() |
341df6c6a3 | ||
![]() |
ab8fb82c1b | ||
![]() |
22d9173cea | ||
![]() |
05720e63ab | ||
![]() |
cdc2b23257 | ||
![]() |
9f0e89719c | ||
![]() |
190726e61c | ||
![]() |
729fafdb48 | ||
![]() |
b7ae23ac64 | ||
![]() |
27885f2c86 | ||
![]() |
ce9046c7b5 | ||
![]() |
506e6ce017 | ||
![]() |
f07b439731 | ||
![]() |
ac70ba8424 | ||
![]() |
84ec1ef418 | ||
![]() |
b576a9de3d | ||
![]() |
148faa00e4 | ||
![]() |
e82b471c14 | ||
![]() |
9ed92e5117 | ||
![]() |
303b3aa354 | ||
![]() |
d801a50962 | ||
![]() |
9cd8a75dc6 | ||
![]() |
002dfd5d58 | ||
![]() |
54e54b2a8a | ||
![]() |
aa768596a4 | ||
![]() |
c4958f6c54 | ||
![]() |
2c2a5314d4 | ||
![]() |
669516c60b | ||
![]() |
a56a29a6c4 | ||
![]() |
4e31f47482 | ||
![]() |
c1f14f9653 | ||
![]() |
2746c52d7b | ||
![]() |
5df323bacb | ||
![]() |
776cc26377 | ||
![]() |
bdfb6a90b6 | ||
![]() |
6db44dfab1 | ||
![]() |
c68ac7db6d | ||
![]() |
e09862e940 | ||
![]() |
c7bd7469a1 | ||
![]() |
b39857fd2e | ||
![]() |
8170f823ab | ||
![]() |
38c0ead45c | ||
![]() |
1d027c1694 | ||
![]() |
45dc21fbf7 | ||
![]() |
d4f4de234a | ||
![]() |
394d5471e3 | ||
![]() |
beb098adb3 | ||
![]() |
fda370d35b | ||
![]() |
99681e1bbb | ||
![]() |
21f44380b1 | ||
![]() |
f4c1af1bb8 | ||
![]() |
c002b81ebd | ||
![]() |
d2f07ba3b6 | ||
![]() |
24fc27b09e | ||
![]() |
cf4ff856d5 | ||
![]() |
b01de1ad6e | ||
![]() |
5fb1bcb552 | ||
![]() |
b937ba38b6 | ||
![]() |
849718fdc7 | ||
![]() |
a6372f701d | ||
![]() |
12f135bb14 | ||
![]() |
16870fcbb9 | ||
![]() |
2fbd152fb3 | ||
![]() |
a7b178e024 | ||
![]() |
c5a035437b | ||
![]() |
75c550fd19 | ||
![]() |
1b680344b1 | ||
![]() |
3329636d32 | ||
![]() |
77e9bf7c43 | ||
![]() |
40f5c9365e | ||
![]() |
850ba7efef | ||
![]() |
548369ca0b | ||
![]() |
afc0d5b7bc |
@@ -3,13 +3,11 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
working_directory: ~/code
|
working_directory: ~/code
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-28-ndk
|
- image: cimg/android:2022.03-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:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@
|
|||||||
/app/alphaStagenet
|
/app/alphaStagenet
|
||||||
/app/prodStagenet
|
/app/prodStagenet
|
||||||
/app/.cxx
|
/app/.cxx
|
||||||
|
/monerujo.id
|
||||||
|
@@ -121,7 +121,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 +171,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 +197,7 @@ target_link_libraries( monerujo
|
|||||||
wallet
|
wallet
|
||||||
cryptonote_core
|
cryptonote_core
|
||||||
cryptonote_basic
|
cryptonote_basic
|
||||||
|
cryptonote_format_utils_basic
|
||||||
mnemonics
|
mnemonics
|
||||||
ringct
|
ringct
|
||||||
ringct_basic
|
ringct_basic
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 31
|
||||||
buildToolsVersion '29.0.3'
|
buildToolsVersion '30.0.3'
|
||||||
|
ndkVersion '17.2.4988734'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 31
|
||||||
versionCode 708
|
versionCode 3004
|
||||||
versionName "1.17.8 'Druk'"
|
versionName "3.0.4 'Fluorine Fermi'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -71,7 +72,7 @@ android {
|
|||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
||||||
universalApk true
|
universalApk true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,44 +111,49 @@ 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'
|
||||||
}
|
}
|
||||||
|
|
||||||
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 'androidx.core:core:1.7.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
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.3'
|
||||||
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
|
|
||||||
|
implementation 'com.google.android.material:material:1.6.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.9.3"
|
||||||
implementation "com.burgstaller:okhttp-digest:2.1"
|
implementation "io.github.rburgst:okhttp-digest:2.6"
|
||||||
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.5.2'
|
||||||
|
|
||||||
implementation 'dnsjava:dnsjava:2.1.9'
|
implementation 'dnsjava:dnsjava:2.1.9'
|
||||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
implementation 'org.jitsi:dnssecjava:1.2.0'
|
||||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
implementation 'org.slf4j:slf4j-nop:1.7.36'
|
||||||
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.9.3"
|
||||||
|
testImplementation 'org.json:json:20211205'
|
||||||
|
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||||
|
|
||||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
compileOnly 'org.projectlombok:lombok:1.18.22'
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
|
||||||
testImplementation 'org.json:json:20180813'
|
|
||||||
testImplementation 'net.jodah:concurrentunit:0.4.4'
|
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.16'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,50 @@
|
|||||||
<?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-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.intent.action.START" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.intent.action.STATUS" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.REQUEST_V3_ONION_SERVICE" />
|
||||||
|
</intent>
|
||||||
|
|
||||||
|
<package android:name="org.torproject.android" />
|
||||||
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:name=".XmrWalletApplication"
|
android:name=".XmrWalletApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:preserveLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/MyMaterialTheme"
|
android:theme="@style/MyMaterialThemeClassic"
|
||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||||
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="portrait">
|
android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@@ -36,27 +53,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" />
|
||||||
@@ -89,4 +107,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
@@ -78,7 +78,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
|
||||||
|
@@ -27,14 +27,16 @@ import android.nfc.NfcAdapter;
|
|||||||
import android.nfc.Tag;
|
import android.nfc.Tag;
|
||||||
import android.nfc.tech.Ndef;
|
import android.nfc.tech.Ndef;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||||
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
||||||
@@ -46,7 +48,8 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class BaseActivity extends SecureActivity implements GenerateReviewFragment.ProgressListener {
|
public class BaseActivity extends SecureActivity
|
||||||
|
implements GenerateReviewFragment.ProgressListener, SubaddressFragment.ProgressListener {
|
||||||
|
|
||||||
ProgressDialog progressDialog = null;
|
ProgressDialog progressDialog = null;
|
||||||
|
|
||||||
@@ -178,7 +181,7 @@ public class BaseActivity extends SecureActivity implements GenerateReviewFragme
|
|||||||
return;
|
return;
|
||||||
nfcPendingIntent = PendingIntent.getActivity(this, 0,
|
nfcPendingIntent = PendingIntent.getActivity(this, 0,
|
||||||
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||||
0);
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processNfcIntent(Intent intent) {
|
private void processNfcIntent(Intent intent) {
|
||||||
|
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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
@@ -59,7 +58,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;
|
||||||
@@ -219,8 +217,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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -228,24 +226,23 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
// open up edit dialog
|
// open up edit dialog
|
||||||
@Override
|
@Override
|
||||||
public void onLongInteraction(final View view, final NodeInfo nodeItem) {
|
public boolean onLongInteraction(final View view, final NodeInfo nodeItem) {
|
||||||
Timber.d("onLongInteraction");
|
Timber.d("onLongInteraction");
|
||||||
EditDialog diag = createEditDialog(nodeItem);
|
EditDialog diag = createEditDialog(nodeItem);
|
||||||
if (diag != null) {
|
if (diag != null) {
|
||||||
diag.show();
|
diag.show();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int id = v.getId();
|
int id = v.getId();
|
||||||
switch (id) {
|
if (id == R.id.fab) {
|
||||||
case R.id.fab:
|
EditDialog diag = createEditDialog(null);
|
||||||
EditDialog diag = createEditDialog(null);
|
if (diag != null) {
|
||||||
if (diag != null) {
|
diag.show();
|
||||||
diag.show();
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,16 +401,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) {
|
||||||
@@ -524,7 +517,7 @@ public class NodeFragment extends Fragment
|
|||||||
.setNegativeButton(getString(R.string.label_cancel),
|
.setNegativeButton(getString(R.string.label_cancel),
|
||||||
(dialog, id) -> {
|
(dialog, id) -> {
|
||||||
closeDialog();
|
closeDialog();
|
||||||
nodesAdapter.dataSetChanged(); // to refresh test results
|
nodesAdapter.setNodes(); // to refresh test results
|
||||||
});
|
});
|
||||||
|
|
||||||
editDialog = alertDialogBuilder.create();
|
editDialog = alertDialogBuilder.create();
|
||||||
@@ -533,20 +526,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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -554,15 +537,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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,8 +569,9 @@ public class NodeFragment extends Fragment
|
|||||||
if (nodeBackup == null) {
|
if (nodeBackup == null) {
|
||||||
nodesAdapter.addNode(nodeInfo);
|
nodesAdapter.addNode(nodeInfo);
|
||||||
} else {
|
} else {
|
||||||
nodesAdapter.dataSetChanged();
|
nodesAdapter.setNodes();
|
||||||
}
|
}
|
||||||
|
nodesAdapter.notifyItemChanged(nodeInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
|
||||||
|
public interface OnBlockUpdateListener {
|
||||||
|
void onBlockUpdate(final Wallet wallet);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
}
|
}
|
||||||
|
125
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
125
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
}
|
242
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal file
242
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.data.Subaddress;
|
||||||
|
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||||
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class SubaddressFragment extends Fragment implements SubaddressInfoAdapter.OnInteractionListener,
|
||||||
|
View.OnClickListener, OnBlockUpdateListener {
|
||||||
|
static public final String KEY_MODE = "mode";
|
||||||
|
static public final String MODE_MANAGER = "manager";
|
||||||
|
|
||||||
|
private SubaddressInfoAdapter adapter;
|
||||||
|
|
||||||
|
private Listener activityCallback;
|
||||||
|
|
||||||
|
private Wallet wallet;
|
||||||
|
|
||||||
|
// Container Activity must implement this interface
|
||||||
|
public interface Listener {
|
||||||
|
void onSubaddressSelected(Subaddress subaddress);
|
||||||
|
|
||||||
|
void setSubtitle(String title);
|
||||||
|
|
||||||
|
void setToolbarButton(int type);
|
||||||
|
|
||||||
|
void showSubaddress(View view, final int subaddressIndex);
|
||||||
|
|
||||||
|
void saveWallet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressListener {
|
||||||
|
void showProgressDialog(int msgId);
|
||||||
|
|
||||||
|
void showLedgerProgressDialog(int mode);
|
||||||
|
|
||||||
|
void dismissProgressDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProgressListener progressCallback = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof ProgressListener) {
|
||||||
|
progressCallback = (ProgressListener) context;
|
||||||
|
}
|
||||||
|
if (context instanceof Listener) {
|
||||||
|
activityCallback = (Listener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString()
|
||||||
|
+ " must implement Listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
Timber.d("onPause()");
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
|
||||||
|
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
Timber.d("onCreateView");
|
||||||
|
|
||||||
|
final Bundle b = getArguments();
|
||||||
|
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
|
||||||
|
|
||||||
|
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
|
||||||
|
view.findViewById(R.id.fab).setOnClickListener(this);
|
||||||
|
|
||||||
|
if (managerMode) {
|
||||||
|
view.findViewById(R.id.tvInstruction).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.tvHint).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final RecyclerView list = view.findViewById(R.id.list);
|
||||||
|
adapter = new SubaddressInfoAdapter(getActivity(), this);
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||||
|
@Override
|
||||||
|
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||||
|
list.scrollToPosition(positionStart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.hideKeyboard(getActivity());
|
||||||
|
|
||||||
|
wallet = WalletManager.getInstance().getWallet();
|
||||||
|
|
||||||
|
loadList();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadList() {
|
||||||
|
Timber.d("loadList()");
|
||||||
|
final int numSubaddresses = wallet.getNumSubaddresses();
|
||||||
|
final List<Subaddress> list = new ArrayList<>();
|
||||||
|
for (int i = 0; i < numSubaddresses; i++) {
|
||||||
|
list.add(wallet.getSubaddressObject(i));
|
||||||
|
}
|
||||||
|
adapter.setInfos(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlockUpdate(Wallet wallet) {
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
int id = v.getId();
|
||||||
|
if (id == R.id.fab) {
|
||||||
|
getNewSubaddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lastUsedSubaddress() {
|
||||||
|
int lastUsedSubaddress = 0;
|
||||||
|
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
||||||
|
if (info.addressIndex > lastUsedSubaddress)
|
||||||
|
lastUsedSubaddress = info.addressIndex;
|
||||||
|
}
|
||||||
|
return lastUsedSubaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getNewSubaddress() {
|
||||||
|
final int maxSubaddresses = lastUsedSubaddress() + wallet.getDeviceType().getSubaddressLookahead();
|
||||||
|
if (wallet.getNumSubaddresses() < maxSubaddresses)
|
||||||
|
new AsyncSubaddress().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||||
|
else
|
||||||
|
Toast.makeText(getActivity(), getString(R.string.max_subaddress_warning), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private class AsyncSubaddress extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
boolean dialogOpened = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
if ((wallet.getDeviceType() == Wallet.Device.Device_Ledger) && (progressCallback != null)) {
|
||||||
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
||||||
|
dialogOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
if (params.length != 0) return false;
|
||||||
|
wallet.getNewSubaddress();
|
||||||
|
if (activityCallback != null) {
|
||||||
|
activityCallback.saveWallet();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
if (dialogOpened)
|
||||||
|
progressCallback.dismissProgressDialog();
|
||||||
|
if (!isAdded()) // never mind then
|
||||||
|
return;
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean managerMode = false;
|
||||||
|
|
||||||
|
// Callbacks from SubaddressInfoAdapter
|
||||||
|
@Override
|
||||||
|
public void onInteraction(final View view, final Subaddress subaddress) {
|
||||||
|
if (managerMode)
|
||||||
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
|
else
|
||||||
|
activityCallback.onSubaddressSelected(subaddress); // also closes the fragment with onBackpressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongInteraction(View view, Subaddress subaddress) {
|
||||||
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.transition.Transition;
|
||||||
|
import androidx.transition.TransitionInflater;
|
||||||
|
|
||||||
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
import com.m2049r.xmrwallet.data.Subaddress;
|
||||||
|
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||||
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class SubaddressInfoFragment extends Fragment
|
||||||
|
implements TransactionInfoAdapter.Listener, OnBlockUpdateListener {
|
||||||
|
private TransactionInfoAdapter adapter;
|
||||||
|
|
||||||
|
private Subaddress subaddress;
|
||||||
|
|
||||||
|
private TextInputLayout etName;
|
||||||
|
private TextView tvAddress;
|
||||||
|
private TextView tvTxLabel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
||||||
|
|
||||||
|
etName = view.findViewById(R.id.etName);
|
||||||
|
tvAddress = view.findViewById(R.id.tvAddress);
|
||||||
|
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
||||||
|
|
||||||
|
final RecyclerView list = view.findViewById(R.id.list);
|
||||||
|
adapter = new TransactionInfoAdapter(getActivity(), this);
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
|
||||||
|
final Wallet wallet = activityCallback.getWallet();
|
||||||
|
|
||||||
|
Bundle b = getArguments();
|
||||||
|
final int subaddressIndex = b.getInt("subaddressIndex");
|
||||||
|
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
||||||
|
|
||||||
|
etName.getEditText().setText(subaddress.getDisplayLabel());
|
||||||
|
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
|
||||||
|
subaddress.getAddressIndex(), subaddress.getSquashedAddress()));
|
||||||
|
|
||||||
|
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
etName.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||||
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||||
|
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||||
|
Helper.hideKeyboard(getActivity());
|
||||||
|
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
|
||||||
|
onRefreshed(wallet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
onRefreshed(wallet);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Transition transform = TransitionInflater.from(requireContext())
|
||||||
|
.inflateTransition(R.transition.details);
|
||||||
|
setSharedElementEnterTransition(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRefreshed(final Wallet wallet) {
|
||||||
|
Timber.d("onRefreshed");
|
||||||
|
List<TransactionInfo> list = new ArrayList<>();
|
||||||
|
for (TransactionInfo info : wallet.getHistory().getAll()) {
|
||||||
|
if (info.addressIndex == subaddress.getAddressIndex())
|
||||||
|
list.add(info);
|
||||||
|
}
|
||||||
|
adapter.setInfos(list);
|
||||||
|
if (list.isEmpty())
|
||||||
|
tvTxLabel.setText(R.string.subaddress_notx_label);
|
||||||
|
else
|
||||||
|
tvTxLabel.setText(R.string.subaddress_tx_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlockUpdate(Wallet wallet) {
|
||||||
|
onRefreshed(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks from TransactionInfoAdapter
|
||||||
|
@Override
|
||||||
|
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||||
|
activityCallback.onTxDetailsRequest(view, infoItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener activityCallback;
|
||||||
|
|
||||||
|
// Container Activity must implement this interface
|
||||||
|
public interface Listener {
|
||||||
|
void onTxDetailsRequest(View view, TransactionInfo info);
|
||||||
|
|
||||||
|
Wallet getWallet();
|
||||||
|
|
||||||
|
void setToolbarButton(int type);
|
||||||
|
|
||||||
|
void setTitle(String title, String subtitle);
|
||||||
|
|
||||||
|
void setSubtitle(String subtitle);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof Listener) {
|
||||||
|
this.activityCallback = (Listener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context.toString()
|
||||||
|
+ " must implement Listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Timber.d("onResume()");
|
||||||
|
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
|
||||||
|
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDaemonHeight() {
|
||||||
|
return activityCallback.getDaemonHeight();
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,9 @@ import android.content.Intent;
|
|||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -33,14 +35,19 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
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.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.model.WalletManager;
|
||||||
|
import com.m2049r.xmrwallet.service.WalletService;
|
||||||
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.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -50,6 +57,8 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class TxFragment extends Fragment {
|
public class TxFragment extends Fragment {
|
||||||
|
|
||||||
static public final String ARG_INFO = "info";
|
static public final String ARG_INFO = "info";
|
||||||
@@ -76,6 +85,9 @@ public class TxFragment extends Fragment {
|
|||||||
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;
|
||||||
@@ -86,9 +98,7 @@ public class TxFragment extends Fragment {
|
|||||||
private ImageView tvXmrToLogo;
|
private ImageView tvXmrToLogo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Bundle savedInstanceState) {
|
|
||||||
|
|
||||||
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
|
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
|
||||||
|
|
||||||
cvXmrTo = view.findViewById(R.id.cvXmrTo);
|
cvXmrTo = view.findViewById(R.id.cvXmrTo);
|
||||||
@@ -111,17 +121,18 @@ public class TxFragment extends Fragment {
|
|||||||
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(getActivity(), 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();
|
||||||
});
|
});
|
||||||
|
|
||||||
Bundle args = getArguments();
|
info = getArguments().getParcelable(ARG_INFO);
|
||||||
TransactionInfo info = args.getParcelable(ARG_INFO);
|
show();
|
||||||
show(info);
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +201,7 @@ public class TxFragment extends Fragment {
|
|||||||
TransactionInfo info = null;
|
TransactionInfo info = null;
|
||||||
UserNotes userNotes = null;
|
UserNotes userNotes = null;
|
||||||
|
|
||||||
void loadNotes(TransactionInfo info) {
|
void loadNotes() {
|
||||||
if ((userNotes == null) || (info.notes == null)) {
|
if ((userNotes == null) || (info.notes == null)) {
|
||||||
info.notes = activityCallback.getTxNotes(info.hash);
|
info.notes = activityCallback.getTxNotes(info.hash);
|
||||||
}
|
}
|
||||||
@@ -203,19 +214,28 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxFee.setTextColor(clr);
|
tvTxFee.setTextColor(clr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void show(TransactionInfo info) {
|
private void showSubaddressLabel() {
|
||||||
|
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
||||||
|
final Context ctx = getContext();
|
||||||
|
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
||||||
|
info.accountIndex, info.addressIndex,
|
||||||
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
subaddress.getDisplayLabel()));
|
||||||
|
tvAccount.setText(label);
|
||||||
|
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show() {
|
||||||
if (info.txKey == null) {
|
if (info.txKey == null) {
|
||||||
info.txKey = activityCallback.getTxKey(info.hash);
|
info.txKey = activityCallback.getTxKey(info.hash);
|
||||||
}
|
}
|
||||||
if (info.address == null) {
|
if (info.address == null) {
|
||||||
info.address = activityCallback.getTxAddress(info.account, info.subaddress);
|
info.address = activityCallback.getTxAddress(info.accountIndex, info.addressIndex);
|
||||||
}
|
}
|
||||||
loadNotes(info);
|
loadNotes();
|
||||||
|
|
||||||
activityCallback.setSubtitle(getString(R.string.tx_title));
|
showSubaddressLabel();
|
||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
|
||||||
|
|
||||||
tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress));
|
|
||||||
tvAddress.setText(info.address);
|
tvAddress.setText(info.address);
|
||||||
|
|
||||||
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
|
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
|
||||||
@@ -245,17 +265,17 @@ public class TxFragment extends Fragment {
|
|||||||
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(getContext(), R.attr.neutralColor));
|
||||||
} else if (info.isPending) {
|
} else if (info.isPending) {
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_pending));
|
setTxColour(ThemeHelper.getThemedColor(getContext(), 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(getContext(), R.attr.positiveColor));
|
||||||
} else {
|
} else {
|
||||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus));
|
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.negativeColor));
|
||||||
}
|
}
|
||||||
Set<String> destinations = new HashSet<>();
|
Set<String> destinations = new HashSet<>();
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
StringBuffer dstSb = new StringBuffer();
|
StringBuilder dstSb = new StringBuilder();
|
||||||
if (info.transfers != null) {
|
if (info.transfers != null) {
|
||||||
boolean newline = false;
|
boolean newline = false;
|
||||||
for (Transfer transfer : info.transfers) {
|
for (Transfer transfer : info.transfers) {
|
||||||
@@ -279,15 +299,27 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sb.append("-");
|
sb.append("-");
|
||||||
dstSb.append(info.direction ==
|
dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ?
|
||||||
TransactionInfo.Direction.Direction_In ?
|
activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex).getAddress() :
|
||||||
activityCallback.getWalletSubaddress(info.account, info.subaddress) :
|
|
||||||
"-");
|
"-");
|
||||||
}
|
}
|
||||||
tvTxTransfers.setText(sb.toString());
|
tvTxTransfers.setText(sb.toString());
|
||||||
tvDestination.setText(dstSb.toString());
|
tvDestination.setText(dstSb.toString());
|
||||||
this.info = info;
|
|
||||||
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")
|
||||||
@@ -300,7 +332,7 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
tvTxXmrToKey.setText(key);
|
tvTxXmrToKey.setText(key);
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||||
switch (userNotes.xmrtoTag) {
|
switch (userNotes.xmrtoTag) {
|
||||||
case "xmrto":
|
case "xmrto":
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
@@ -329,6 +361,9 @@ 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);
|
||||||
|
Transition transform = TransitionInflater.from(requireContext())
|
||||||
|
.inflateTransition(R.transition.details);
|
||||||
|
setSharedElementEnterTransition(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -340,7 +375,7 @@ public class TxFragment extends Fragment {
|
|||||||
Listener activityCallback;
|
Listener activityCallback;
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
String getWalletSubaddress(int accountIndex, int subaddressIndex);
|
Subaddress getWalletSubaddress(int accountIndex, int subaddressIndex);
|
||||||
|
|
||||||
String getTxKey(String hash);
|
String getTxKey(String hash);
|
||||||
|
|
||||||
@@ -354,6 +389,9 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
void setSubtitle(String subtitle);
|
void setSubtitle(String subtitle);
|
||||||
|
|
||||||
|
void showSubaddress(View view, final int subaddressIndex);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -378,4 +416,13 @@ public class TxFragment extends Fragment {
|
|||||||
Helper.hideKeyboard(getActivity());
|
Helper.hideKeyboard(getActivity());
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Timber.d("onResume()");
|
||||||
|
activityCallback.setSubtitle(getString(R.string.tx_title));
|
||||||
|
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
|
showSubaddressLabel();
|
||||||
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,22 +21,29 @@ 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 androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
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();
|
||||||
|
FragmentManager.enableNewStateManager(false);
|
||||||
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 +52,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));
|
||||||
|
@@ -28,7 +28,11 @@ public enum DefaultNodes {
|
|||||||
SUPPORTXMR("node.supportxmr.com:18081"),
|
SUPPORTXMR("node.supportxmr.com:18081"),
|
||||||
HASHVAULT("nodes.hashvault.pro:18081"),
|
HASHVAULT("nodes.hashvault.pro:18081"),
|
||||||
MONEROWORLD("node.moneroworld.com:18089"),
|
MONEROWORLD("node.moneroworld.com:18089"),
|
||||||
XMRTW("opennode.xmr-tw.org:18089");
|
XMRTW("opennode.xmr-tw.org:18089"),
|
||||||
|
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
||||||
|
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
|
||||||
|
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
|
||||||
|
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final String uri;
|
private final String uri;
|
||||||
|
@@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
|
|||||||
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
import com.m2049r.xmrwallet.util.OnionHelper;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -35,11 +36,63 @@ public class Node {
|
|||||||
static public final String STAGENET = "stagenet";
|
static public final String STAGENET = "stagenet";
|
||||||
static public final String TESTNET = "testnet";
|
static public final String TESTNET = "testnet";
|
||||||
|
|
||||||
|
static class Address {
|
||||||
|
final private InetAddress inet;
|
||||||
|
final private String onion;
|
||||||
|
|
||||||
|
public boolean isOnion() {
|
||||||
|
return onion != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHostName() {
|
||||||
|
if (inet != null) {
|
||||||
|
return inet.getHostName();
|
||||||
|
} else {
|
||||||
|
return onion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHostAddress() {
|
||||||
|
if (inet != null) {
|
||||||
|
return inet.getHostAddress();
|
||||||
|
} else {
|
||||||
|
return onion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address(InetAddress address, String onion) {
|
||||||
|
this.inet = address;
|
||||||
|
this.onion = onion;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Address of(InetAddress address) {
|
||||||
|
return new Address(address, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Address of(String host) throws UnknownHostException {
|
||||||
|
if (OnionHelper.isOnionHost(host)) {
|
||||||
|
return new Address(null, host);
|
||||||
|
} else {
|
||||||
|
return new Address(InetAddress.getByName(host), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getHostAddress().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return (other instanceof Address) && (getHostAddress().equals(((Address) other).getHostAddress()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String name = null;
|
private String name = null;
|
||||||
@Getter
|
@Getter
|
||||||
final private NetworkType networkType;
|
final private NetworkType networkType;
|
||||||
InetAddress hostAddress;
|
Address hostAddress;
|
||||||
@Getter
|
@Getter
|
||||||
private String host;
|
private String host;
|
||||||
@Getter
|
@Getter
|
||||||
@@ -64,12 +117,18 @@ public class Node {
|
|||||||
return hostAddress.hashCode();
|
return hostAddress.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes are equal if they are the same host address & are on the same network
|
// Nodes are equal if they are the same host address:port & are on the same network
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (!(other instanceof Node)) return false;
|
if (!(other instanceof Node)) return false;
|
||||||
final Node anotherNode = (Node) other;
|
final Node anotherNode = (Node) other;
|
||||||
return (hostAddress.equals(anotherNode.hostAddress) && (networkType == anotherNode.networkType));
|
return (hostAddress.equals(anotherNode.hostAddress)
|
||||||
|
&& (rpcPort == anotherNode.rpcPort)
|
||||||
|
&& (networkType == anotherNode.networkType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnion() {
|
||||||
|
return hostAddress.isOnion();
|
||||||
}
|
}
|
||||||
|
|
||||||
static public Node fromString(String nodeString) {
|
static public Node fromString(String nodeString) {
|
||||||
@@ -203,7 +262,7 @@ public class Node {
|
|||||||
// constructor used for created nodes from retrieved peer lists
|
// constructor used for created nodes from retrieved peer lists
|
||||||
public Node(InetSocketAddress socketAddress) {
|
public Node(InetSocketAddress socketAddress) {
|
||||||
this();
|
this();
|
||||||
this.hostAddress = socketAddress.getAddress();
|
this.hostAddress = Address.of(socketAddress.getAddress());
|
||||||
this.host = socketAddress.getHostString();
|
this.host = socketAddress.getHostString();
|
||||||
this.rpcPort = 0; // unknown
|
this.rpcPort = 0; // unknown
|
||||||
this.levinPort = socketAddress.getPort();
|
this.levinPort = socketAddress.getPort();
|
||||||
@@ -223,17 +282,25 @@ public class Node {
|
|||||||
if ((host == null) || (host.isEmpty()))
|
if ((host == null) || (host.isEmpty()))
|
||||||
throw new UnknownHostException("loopback not supported (yet?)");
|
throw new UnknownHostException("loopback not supported (yet?)");
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.hostAddress = InetAddress.getByName(host);
|
this.hostAddress = Address.of(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName() {
|
public void setDefaultName() {
|
||||||
if (name == null)
|
if (name != null) return;
|
||||||
this.name = hostAddress.getHostName();
|
String nodeName = hostAddress.getHostName();
|
||||||
|
if (hostAddress.isOnion()) {
|
||||||
|
nodeName = nodeName.substring(0, nodeName.length() - ".onion".length());
|
||||||
|
if (nodeName.length() > 16) {
|
||||||
|
nodeName = nodeName.substring(0, 8) + "…" + nodeName.substring(nodeName.length() - 6);
|
||||||
|
}
|
||||||
|
nodeName = nodeName + ".onion";
|
||||||
|
}
|
||||||
|
this.name = nodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
if ((name == null) || (name.isEmpty()))
|
if ((name == null) || (name.isEmpty()))
|
||||||
this.name = hostAddress.getHostName();
|
setDefaultName();
|
||||||
else
|
else
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
@@ -16,14 +16,19 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.data;
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
import com.burgstaller.okhttp.AuthenticationCacheInterceptor;
|
import android.content.Context;
|
||||||
import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
|
import android.text.Html;
|
||||||
import com.burgstaller.okhttp.digest.CachingAuthenticator;
|
import android.text.Spanned;
|
||||||
import com.burgstaller.okhttp.digest.Credentials;
|
import android.widget.TextView;
|
||||||
import com.burgstaller.okhttp.digest.DigestAuthenticator;
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.m2049r.levin.scanner.LevinPeer;
|
import com.m2049r.levin.scanner.LevinPeer;
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper.Request;
|
||||||
import com.m2049r.xmrwallet.util.NodePinger;
|
import com.m2049r.xmrwallet.util.NodePinger;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -32,17 +37,12 @@ import java.io.IOException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -94,7 +94,7 @@ public class NodeInfo extends Node {
|
|||||||
synchronized public SocketAddress getLevinSocketAddress() {
|
synchronized public SocketAddress getLevinSocketAddress() {
|
||||||
if (levinSocketAddress == null) {
|
if (levinSocketAddress == null) {
|
||||||
// use default peer port if not set - very few peers use nonstandard port
|
// use default peer port if not set - very few peers use nonstandard port
|
||||||
levinSocketAddress = new InetSocketAddress(hostAddress, getDefaultLevinPort());
|
levinSocketAddress = new InetSocketAddress(hostAddress.getHostAddress(), getDefaultLevinPort());
|
||||||
}
|
}
|
||||||
return levinSocketAddress;
|
return levinSocketAddress;
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ public class NodeInfo extends Node {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
|
private static final int HTTP_TIMEOUT = 1000; //ms
|
||||||
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
|
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
|
||||||
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
||||||
public static final double PING_BAD = HTTP_TIMEOUT;
|
public static final double PING_BAD = HTTP_TIMEOUT;
|
||||||
@@ -196,39 +196,35 @@ public class NodeInfo extends Node {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Request rpcServiceRequest(int port) {
|
||||||
|
final HttpUrl url = new HttpUrl.Builder()
|
||||||
|
.scheme("http")
|
||||||
|
.host(getHost())
|
||||||
|
.port(port)
|
||||||
|
.addPathSegment("json_rpc")
|
||||||
|
.build();
|
||||||
|
final String json = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}";
|
||||||
|
return new Request(url, json, getUsername(), getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean testRpcService(int port) {
|
private boolean testRpcService(int port) {
|
||||||
Timber.d("Testing %s", toNodeString());
|
Timber.d("Testing %s", toNodeString());
|
||||||
clear();
|
clear();
|
||||||
|
if (hostAddress.isOnion() && !NetCipherHelper.isTor()) {
|
||||||
|
tested = true; // sortof
|
||||||
|
responseCode = 418; // I'm a teapot - or I need an Onion - who knows
|
||||||
|
return false; // autofail
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
OkHttpClient client = OkHttpHelper.getEagerClient();
|
|
||||||
if (!getUsername().isEmpty()) {
|
|
||||||
final DigestAuthenticator authenticator =
|
|
||||||
new DigestAuthenticator(new Credentials(getUsername(), getPassword()));
|
|
||||||
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
|
|
||||||
client = client.newBuilder()
|
|
||||||
.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
|
|
||||||
.addInterceptor(new AuthenticationCacheInterceptor(authCache))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
HttpUrl url = new HttpUrl.Builder()
|
|
||||||
.scheme("http")
|
|
||||||
.host(getHostAddress())
|
|
||||||
.port(port)
|
|
||||||
.addPathSegment("json_rpc")
|
|
||||||
.build();
|
|
||||||
final RequestBody reqBody = RequestBody
|
|
||||||
.create(MediaType.parse("application/json"),
|
|
||||||
"{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}");
|
|
||||||
Request request = OkHttpHelper.getPostRequest(url, reqBody);
|
|
||||||
long ta = System.nanoTime();
|
long ta = System.nanoTime();
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = rpcServiceRequest(port).execute()) {
|
||||||
|
Timber.d("%s: %s", response.code(), response.request().url());
|
||||||
responseTime = (System.nanoTime() - ta) / 1000000.0;
|
responseTime = (System.nanoTime() - ta) / 1000000.0;
|
||||||
responseCode = response.code();
|
responseCode = response.code();
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
ResponseBody respBody = response.body(); // closed through Response object
|
ResponseBody respBody = response.body(); // closed through Response object
|
||||||
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
|
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
|
||||||
final JSONObject json = new JSONObject(
|
final JSONObject json = new JSONObject(respBody.string());
|
||||||
respBody.string());
|
|
||||||
String rpcVersion = json.getString("jsonrpc");
|
String rpcVersion = json.getString("jsonrpc");
|
||||||
if (!RPC_VERSION.equals(rpcVersion))
|
if (!RPC_VERSION.equals(rpcVersion))
|
||||||
return false;
|
return false;
|
||||||
@@ -244,7 +240,7 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | JSONException ex) {
|
} catch (IOException | JSONException ex) {
|
||||||
Timber.d(ex);
|
Timber.d("EX: %s", ex.getMessage()); //TODO: do something here (show error?)
|
||||||
} finally {
|
} finally {
|
||||||
tested = true;
|
tested = true;
|
||||||
}
|
}
|
||||||
@@ -265,4 +261,43 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public final int STALE_NODE_HOURS = 2;
|
||||||
|
|
||||||
|
public void showInfo(TextView view, String info, boolean isError) {
|
||||||
|
final Context ctx = view.getContext();
|
||||||
|
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
||||||
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
(hostAddress.isOnion() ? " .onion " : ""), " " + info));
|
||||||
|
view.setText(text);
|
||||||
|
if (isError)
|
||||||
|
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorError));
|
||||||
|
else
|
||||||
|
view.setTextColor(ThemeHelper.getThemedColor(ctx, android.R.attr.textColorSecondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInfo(TextView view) {
|
||||||
|
if (!isTested()) {
|
||||||
|
showInfo(view, "", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Context ctx = view.getContext();
|
||||||
|
final long now = Calendar.getInstance().getTimeInMillis() / 1000;
|
||||||
|
final long secs = (now - timestamp);
|
||||||
|
final long mins = secs / 60;
|
||||||
|
final long hours = mins / 60;
|
||||||
|
final long days = hours / 24;
|
||||||
|
String info;
|
||||||
|
if (mins < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_now, secs);
|
||||||
|
} else if (hours < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_mins, mins);
|
||||||
|
} else if (days < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_hours, hours);
|
||||||
|
} else {
|
||||||
|
info = ctx.getString(R.string.node_updated_days, days);
|
||||||
|
}
|
||||||
|
showInfo(view, info, hours >= STALE_NODE_HOURS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
63
app/src/main/java/com/m2049r/xmrwallet/data/Subaddress.java
Normal file
63
app/src/main/java/com/m2049r/xmrwallet/data/Subaddress.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class Subaddress implements Comparable<Subaddress> {
|
||||||
|
@Getter
|
||||||
|
final private int accountIndex;
|
||||||
|
@Getter
|
||||||
|
final private int addressIndex;
|
||||||
|
@Getter
|
||||||
|
final private String address;
|
||||||
|
@Getter
|
||||||
|
private final String label;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private long amount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Subaddress another) { // newer is <
|
||||||
|
final int compareAccountIndex = another.accountIndex - accountIndex;
|
||||||
|
if (compareAccountIndex == 0)
|
||||||
|
return another.addressIndex - addressIndex;
|
||||||
|
return compareAccountIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSquashedAddress() {
|
||||||
|
return address.substring(0, 8) + "…" + address.substring(address.length() - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Pattern DEFAULT_LABEL_FORMATTER = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}:[0-9]{2}:[0-9]{2}$");
|
||||||
|
|
||||||
|
public String getDisplayLabel() {
|
||||||
|
if (label.isEmpty() || (DEFAULT_LABEL_FORMATTER.matcher(label).matches()))
|
||||||
|
return ("#" + addressIndex);
|
||||||
|
else
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
@@ -21,6 +21,7 @@ import android.os.Parcelable;
|
|||||||
|
|
||||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
||||||
public class TxData implements Parcelable {
|
public class TxData implements Parcelable {
|
||||||
@@ -54,7 +55,7 @@ public class TxData implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getAmountAsDouble() {
|
public double getAmountAsDouble() {
|
||||||
return 1.0 * amount / 1000000000000L;
|
return 1.0 * amount / Helper.ONE_XMR;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMixin() {
|
public int getMixin() {
|
||||||
|
@@ -17,13 +17,17 @@
|
|||||||
package com.m2049r.xmrwallet.dialog;
|
package com.m2049r.xmrwallet.dialog;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -31,15 +35,20 @@ import androidx.fragment.app.FragmentTransaction;
|
|||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
|
||||||
public class HelpFragment extends DialogFragment {
|
public class HelpFragment extends DialogFragment {
|
||||||
static final String TAG = "HelpFragment";
|
static final String TAG = "HelpFragment";
|
||||||
private static final String HELP_ID = "HELP_ID";
|
private static final String HELP_ID = "HELP_ID";
|
||||||
|
private static final String TOR_BUTTON = "TOR";
|
||||||
|
|
||||||
public static HelpFragment newInstance(int helpResourceId) {
|
public static HelpFragment newInstance(int helpResourceId) {
|
||||||
HelpFragment fragment = new HelpFragment();
|
HelpFragment fragment = new HelpFragment();
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putInt(HELP_ID, helpResourceId);
|
bundle.putInt(HELP_ID, helpResourceId);
|
||||||
|
// a hack for the tor button
|
||||||
|
if (helpResourceId == R.string.help_tor)
|
||||||
|
bundle.putInt(TOR_BUTTON, 7);
|
||||||
fragment.setArguments(bundle);
|
fragment.setArguments(bundle);
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
@@ -54,27 +63,53 @@ public class HelpFragment extends DialogFragment {
|
|||||||
HelpFragment.newInstance(helpResourceId).show(ft, TAG);
|
HelpFragment.newInstance(helpResourceId).show(ft, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Spanned getHtml(String html, double textSize) {
|
||||||
|
final Html.ImageGetter imageGetter = source -> {
|
||||||
|
final int imageId = getResources().getIdentifier(source.replace("/", ""), "drawable", requireActivity().getPackageName());
|
||||||
|
// Don't die if we don't find the image - use a heart instead
|
||||||
|
final Drawable drawable = ContextCompat.getDrawable(requireActivity(), imageId > 0 ? imageId : R.drawable.ic_favorite_24dp);
|
||||||
|
final double f = textSize / drawable.getIntrinsicHeight();
|
||||||
|
drawable.setBounds(0, 0, (int) (f * drawable.getIntrinsicWidth()), (int) textSize);
|
||||||
|
return drawable;
|
||||||
|
};
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY, imageGetter, null);
|
||||||
|
} else {
|
||||||
|
return Html.fromHtml(html, imageGetter, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
|
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
|
||||||
|
|
||||||
int helpId = 0;
|
int helpId = 0;
|
||||||
|
boolean torButton = false;
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
helpId = arguments.getInt(HELP_ID);
|
helpId = arguments.getInt(HELP_ID);
|
||||||
|
torButton = arguments.getInt(TOR_BUTTON) > 0;
|
||||||
}
|
}
|
||||||
|
final TextView helpTv = view.findViewById(R.id.tvHelp);
|
||||||
if (helpId > 0)
|
if (helpId > 0)
|
||||||
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getString(helpId)));
|
helpTv.setText(getHtml(getString(helpId), helpTv.getTextSize()));
|
||||||
|
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
MaterialAlertDialogBuilder builder =
|
||||||
.setView(view)
|
new MaterialAlertDialogBuilder(requireActivity())
|
||||||
.setNegativeButton(R.string.help_ok,
|
.setView(view);
|
||||||
new DialogInterface.OnClickListener() {
|
if (torButton) {
|
||||||
@Override
|
builder.setNegativeButton(R.string.help_nok,
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
(dialog, id) -> dialog.dismiss())
|
||||||
|
.setPositiveButton(R.string.help_getorbot,
|
||||||
|
(dialog, id) -> {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
NetCipherHelper.getInstance().installOrbot(requireActivity());
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
builder.setNegativeButton(R.string.help_ok,
|
||||||
|
(dialog, id) -> dialog.dismiss());
|
||||||
|
}
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -22,6 +22,7 @@ import android.os.Bundle;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -91,6 +92,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
private TextInputLayout etAddress;
|
private TextInputLayout etAddress;
|
||||||
private TextInputLayout etNotes;
|
private TextInputLayout etNotes;
|
||||||
private TextView tvXmrTo;
|
private TextView tvXmrTo;
|
||||||
|
private TextView tvTor;
|
||||||
private Map<Crypto, ImageButton> ibCrypto;
|
private Map<Crypto, ImageButton> ibCrypto;
|
||||||
final private Set<Crypto> possibleCryptos = new HashSet<>();
|
final private Set<Crypto> possibleCryptos = new HashSet<>();
|
||||||
private Crypto selectedCrypto = null;
|
private Crypto selectedCrypto = null;
|
||||||
@@ -117,29 +119,39 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
View view = inflater.inflate(R.layout.fragment_send_address, container, false);
|
View view = inflater.inflate(R.layout.fragment_send_address, container, false);
|
||||||
|
|
||||||
|
tvTor = view.findViewById(R.id.tvTor);
|
||||||
tvXmrTo = view.findViewById(R.id.tvXmrTo);
|
tvXmrTo = view.findViewById(R.id.tvXmrTo);
|
||||||
|
|
||||||
ibCrypto = new HashMap<>();
|
ibCrypto = new HashMap<>();
|
||||||
for (Crypto crypto : Crypto.values()) {
|
for (Crypto crypto : Crypto.values()) {
|
||||||
final ImageButton button = view.findViewById(crypto.getButtonId());
|
final ImageButton button = view.findViewById(crypto.getButtonId());
|
||||||
ibCrypto.put(crypto, button);
|
if (Helper.ALLOW_SHIFT || (crypto == Crypto.XMR)) {
|
||||||
button.setOnClickListener(v -> {
|
ibCrypto.put(crypto, button);
|
||||||
if (possibleCryptos.contains(crypto)) {
|
button.setOnClickListener(v -> {
|
||||||
selectedCrypto = crypto;
|
if (possibleCryptos.contains(crypto)) {
|
||||||
updateCryptoButtons(false);
|
selectedCrypto = crypto;
|
||||||
} else {
|
updateCryptoButtons(false);
|
||||||
// show help what to do:
|
|
||||||
if (button.getId() != R.id.ibXMR) {
|
|
||||||
final String name = getResources().getStringArray(R.array.cryptos)[crypto.ordinal()];
|
|
||||||
final String symbol = getCryptoForButton(button).getSymbol();
|
|
||||||
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help, name, symbol)));
|
|
||||||
tvXmrTo.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
} else {
|
||||||
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help_xmr)));
|
// show help what to do:
|
||||||
tvXmrTo.setVisibility(View.VISIBLE);
|
if (button.getId() != R.id.ibXMR) {
|
||||||
|
final String name = getResources().getStringArray(R.array.cryptos)[crypto.ordinal()];
|
||||||
|
final String symbol = getCryptoForButton(button).getSymbol();
|
||||||
|
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help, name, symbol)));
|
||||||
|
tvXmrTo.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help_xmr)));
|
||||||
|
tvXmrTo.setVisibility(View.VISIBLE);
|
||||||
|
tvTor.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
} else {
|
||||||
|
button.setImageResource(crypto.getIconDisabledId());
|
||||||
|
button.setImageAlpha(128);
|
||||||
|
button.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Helper.ALLOW_SHIFT) {
|
||||||
|
tvTor.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
updateCryptoButtons(true);
|
updateCryptoButtons(true);
|
||||||
|
|
||||||
@@ -181,6 +193,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
selectedCrypto = Crypto.XMR;
|
selectedCrypto = Crypto.XMR;
|
||||||
sendListener.setMode(SendFragment.Mode.XMR);
|
sendListener.setMode(SendFragment.Mode.XMR);
|
||||||
}
|
}
|
||||||
|
if (!Helper.ALLOW_SHIFT) return;
|
||||||
if ((selectedCrypto == null) && isEthAddress(address)) {
|
if ((selectedCrypto == null) && isEthAddress(address)) {
|
||||||
Timber.d("isEthAddress");
|
Timber.d("isEthAddress");
|
||||||
possibleCryptos.add(Crypto.ETH);
|
possibleCryptos.add(Crypto.ETH);
|
||||||
@@ -285,6 +298,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCryptoButtons(boolean noAddress) {
|
private void updateCryptoButtons(boolean noAddress) {
|
||||||
|
if (!Helper.ALLOW_SHIFT) return;
|
||||||
for (Crypto crypto : Crypto.values()) {
|
for (Crypto crypto : Crypto.values()) {
|
||||||
if (crypto == selectedCrypto) {
|
if (crypto == selectedCrypto) {
|
||||||
selectedCrypto(crypto);
|
selectedCrypto(crypto);
|
||||||
@@ -345,7 +359,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private boolean checkAddress() {
|
private boolean checkAddress() {
|
||||||
boolean ok = checkAddressNoError();
|
boolean ok = checkAddressNoError();
|
||||||
if (!ok) {
|
if (possibleCryptos.isEmpty()) {
|
||||||
etAddress.setError(getString(R.string.send_address_invalid));
|
etAddress.setError(getString(R.string.send_address_invalid));
|
||||||
} else {
|
} else {
|
||||||
etAddress.setError(null);
|
etAddress.setError(null);
|
||||||
@@ -447,7 +461,12 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
BarcodeData barcodeData = sendListener.getBarcodeData();
|
BarcodeData barcodeData = sendListener.getBarcodeData();
|
||||||
if (barcodeData != null) {
|
if (barcodeData != null) {
|
||||||
Timber.d("GOT DATA");
|
Timber.d("GOT DATA");
|
||||||
|
if (!Helper.ALLOW_SHIFT && (barcodeData.asset != Crypto.XMR)) {
|
||||||
|
Timber.d("BUT ONLY XMR SUPPORTED");
|
||||||
|
barcodeData = null;
|
||||||
|
sendListener.setBarcodeData(barcodeData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (barcodeData.address != null) {
|
if (barcodeData.address != null) {
|
||||||
etAddress.getEditText().setText(barcodeData.address);
|
etAddress.getEditText().setText(barcodeData.address);
|
||||||
possibleCryptos.clear();
|
possibleCryptos.clear();
|
||||||
@@ -458,7 +477,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
possibleCryptos.add(barcodeData.asset);
|
possibleCryptos.add(barcodeData.asset);
|
||||||
selectedCrypto = barcodeData.asset;
|
selectedCrypto = barcodeData.asset;
|
||||||
}
|
}
|
||||||
updateCryptoButtons(false);
|
if (Helper.ALLOW_SHIFT)
|
||||||
|
updateCryptoButtons(false);
|
||||||
if (checkAddress()) {
|
if (checkAddress()) {
|
||||||
if (barcodeData.security == BarcodeData.Security.OA_NO_DNSSEC)
|
if (barcodeData.security == BarcodeData.Security.OA_NO_DNSSEC)
|
||||||
etAddress.setError(getString(R.string.send_address_no_dnssec));
|
etAddress.setError(getString(R.string.send_address_no_dnssec));
|
||||||
|
@@ -139,7 +139,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
Timber.d("onResumeFragment()");
|
Timber.d("onResumeFragment()");
|
||||||
Helper.showKeyboard(getActivity());
|
Helper.showKeyboard(getActivity());
|
||||||
final long funds = getTotalFunds();
|
final long funds = getTotalFunds();
|
||||||
maxFunds = 1.0 * funds / 1000000000000L;
|
maxFunds = 1.0 * funds / Helper.ONE_XMR;
|
||||||
if (!sendListener.getActivityCallback().isStreetMode()) {
|
if (!sendListener.getActivityCallback().isStreetMode()) {
|
||||||
tvFunds.setText(getString(R.string.send_available,
|
tvFunds.setText(getString(R.string.send_available,
|
||||||
Wallet.getDisplayAmount(funds)));
|
Wallet.getDisplayAmount(funds)));
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user