mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 00:53:36 +02:00
Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
41a7b7ba61 | ||
![]() |
d739d8049e | ||
![]() |
91384b827a | ||
![]() |
386f6e744b | ||
![]() |
1f5ad86b07 | ||
![]() |
ef3a19b66e | ||
![]() |
9fd382e622 | ||
![]() |
f7c1cc9713 | ||
![]() |
30023e85bb | ||
![]() |
bf8780c874 | ||
![]() |
9379269f89 | ||
![]() |
a9b4abc01f | ||
![]() |
899ac775b1 | ||
![]() |
8b7c5a2450 | ||
![]() |
05c1ca5082 | ||
![]() |
6abf11841e | ||
![]() |
d46b8bf79a | ||
![]() |
149fd6376e | ||
![]() |
de70d64eb8 | ||
![]() |
232f7b801e | ||
![]() |
8962bd3050 | ||
![]() |
989d52b33d | ||
![]() |
a3c0ca7ebe | ||
![]() |
c9132d7d97 | ||
![]() |
758b042680 | ||
![]() |
84ce392192 | ||
![]() |
4ebcda2b14 | ||
![]() |
c49351a8a9 | ||
![]() |
41e84f2e29 | ||
![]() |
f08ddf93c8 | ||
![]() |
4bfc9c1bdd | ||
![]() |
1a13bdde91 | ||
![]() |
3e56d5a54b | ||
![]() |
c64fb1a769 | ||
![]() |
2d2a7d2db0 | ||
![]() |
ac9e24ee9f | ||
![]() |
059438a09e | ||
![]() |
d22fdfe2dc | ||
![]() |
48577e46aa | ||
![]() |
451371cd92 | ||
![]() |
cd6f646b63 | ||
![]() |
aa530caa28 | ||
![]() |
5df43f1274 | ||
![]() |
3b0cb3ffdb | ||
![]() |
300551dbe4 | ||
![]() |
942519adb7 | ||
![]() |
0652a8ba14 | ||
![]() |
04d0bd2ffb | ||
![]() |
8473e66c69 | ||
![]() |
2218ff615c | ||
![]() |
54b55b9f8f | ||
![]() |
5abf84f62b | ||
![]() |
6ff75d221f | ||
![]() |
c90f107c3c | ||
![]() |
a3db18032e | ||
![]() |
9db2c10679 | ||
![]() |
eab85c7f0a | ||
![]() |
6bf3229e77 | ||
![]() |
9f4f626acb | ||
![]() |
434dab55ba | ||
![]() |
59cc6b1864 | ||
![]() |
a6e9d0e77c | ||
![]() |
17df7c3faf | ||
![]() |
bf1829f775 | ||
![]() |
bc4aa0f772 | ||
![]() |
3f09e73df7 | ||
![]() |
11b7e23ad2 | ||
![]() |
ae48027689 | ||
![]() |
a42f750fc4 | ||
![]() |
3406f585f2 | ||
![]() |
7546637c89 | ||
![]() |
4da2106f04 | ||
![]() |
93c11fb90e | ||
![]() |
9fa710f75b | ||
![]() |
c53dd300bc | ||
![]() |
97f40648be | ||
![]() |
1ece6bfbeb | ||
![]() |
4b3b99ff2a | ||
![]() |
ca833d7017 | ||
![]() |
f1b6f859de | ||
![]() |
61d19c7066 | ||
![]() |
ffda0e965b | ||
![]() |
cc7cdb383c | ||
![]() |
ac1ea05ef6 | ||
![]() |
1ddd4f30b9 | ||
![]() |
1dc081834f | ||
![]() |
ce084927e1 | ||
![]() |
3610781f43 | ||
![]() |
ef3ddbac71 | ||
![]() |
0512af1496 | ||
![]() |
bd2c49669a | ||
![]() |
ac7831d0f9 | ||
![]() |
0f0b9a38c7 | ||
![]() |
807db19603 | ||
![]() |
c956f38899 | ||
![]() |
db68f517d3 | ||
![]() |
d4b293af80 | ||
![]() |
f7bbfc2fac | ||
![]() |
e08964749e | ||
![]() |
a05fa9d177 | ||
![]() |
7fe2fbe37d | ||
![]() |
40e30fed08 | ||
![]() |
320c7865ff | ||
![]() |
5e8cf8010e | ||
![]() |
e671fa19e0 | ||
![]() |
20d5b9a100 |
@@ -3,7 +3,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
working_directory: ~/code
|
working_directory: ~/code
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/android:2022.03-ndk
|
- image: cimg/android:2023.12-ndk
|
||||||
environment:
|
environment:
|
||||||
JVM_OPTS: -Xmx3200m
|
JVM_OPTS: -Xmx3200m
|
||||||
steps:
|
steps:
|
||||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -8,11 +8,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
/app/build
|
/app/build
|
||||||
/app/release
|
/app/release
|
||||||
/app/alpha
|
/app/alpha*
|
||||||
/app/prod
|
/app/prod*
|
||||||
/app/alphaMainnet
|
|
||||||
/app/prodMainnet
|
|
||||||
/app/alphaStagenet
|
|
||||||
/app/prodStagenet
|
|
||||||
/app/.cxx
|
/app/.cxx
|
||||||
/monerujo.id
|
/monerujo.id
|
||||||
|
/external-libs/VERSION
|
||||||
|
/external-libs/include/wallet2_api.h
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
|
project(monerujo)
|
||||||
message(STATUS ABI_INFO = ${ANDROID_ABI})
|
message(STATUS ABI_INFO = ${ANDROID_ABI})
|
||||||
|
|
||||||
add_library( monerujo
|
add_library( monerujo
|
||||||
@@ -121,7 +122,7 @@ set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
|
|||||||
|
|
||||||
add_library(unbound STATIC IMPORTED)
|
add_library(unbound STATIC IMPORTED)
|
||||||
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
||||||
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libunbound.a)
|
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libunbound.a)
|
||||||
|
|
||||||
add_library(epee STATIC IMPORTED)
|
add_library(epee STATIC IMPORTED)
|
||||||
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
|
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
|
||||||
buildToolsVersion '30.0.3'
|
|
||||||
ndkVersion '17.2.4988734'
|
ndkVersion '17.2.4988734'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
|
buildToolsVersion = '35.0.0'
|
||||||
|
compileSdk 35
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 35
|
||||||
versionCode 1402
|
versionCode 4107
|
||||||
versionName "2.4.2 'Baldaŭ'"
|
versionName "4.1.7 'Exolix'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -24,7 +24,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions 'type', 'net'
|
flavorDimensions = ['type', 'net']
|
||||||
productFlavors {
|
productFlavors {
|
||||||
mainnet {
|
mainnet {
|
||||||
dimension 'net'
|
dimension 'net'
|
||||||
@@ -58,7 +58,7 @@ android {
|
|||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
}
|
}
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
|
variant.buildConfigField "String", "ID_F", "\"" + getId("ID_F") + "\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ android {
|
|||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
||||||
universalApk true
|
universalApk true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,10 +108,25 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
namespace 'com.m2049r.xmrwallet'
|
namespace 'com.m2049r.xmrwallet'
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
all {
|
||||||
|
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
disable "MissingTranslation" // Translation is crowd-sourced.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static def getId(name) {
|
static def getId(name) {
|
||||||
@@ -121,39 +136,42 @@ static def getId(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.core:core:1.7.0'
|
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.21'))
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
|
||||||
|
implementation 'androidx.core:core:1.16.0'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||||
implementation 'androidx.preference:preference:1.2.0'
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.6.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
|
|
||||||
|
implementation 'com.google.guava:guava:33.4.8-android'
|
||||||
|
|
||||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
implementation "com.squareup.okhttp3:okhttp:4.9.3"
|
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||||
implementation "io.github.rburgst:okhttp-digest:2.6"
|
implementation 'io.github.rburgst:okhttp-digest:3.1.1'
|
||||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||||
|
|
||||||
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
|
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
|
||||||
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
|
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
|
||||||
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
|
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
|
||||||
implementation 'com.nulab-inc:zxcvbn:1.5.2'
|
implementation 'com.nulab-inc:zxcvbn:1.9.0'
|
||||||
|
|
||||||
implementation 'dnsjava:dnsjava:2.1.9'
|
implementation 'dnsjava:dnsjava:3.6.3'
|
||||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
implementation 'org.slf4j:slf4j-nop:2.0.17'
|
||||||
implementation 'org.slf4j:slf4j-nop:1.7.36'
|
|
||||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||||
|
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
testImplementation "junit:junit:4.13.2"
|
testImplementation "junit:junit:4.13.2"
|
||||||
testImplementation "org.mockito:mockito-all:1.10.19"
|
testImplementation "org.mockito:mockito-all:1.10.19"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0"
|
||||||
testImplementation 'org.json:json:20211205'
|
testImplementation 'org.json:json:20250517'
|
||||||
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.22'
|
compileOnly 'org.projectlombok:lombok:1.18.38'
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,23 @@
|
|||||||
<?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">
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@@ -95,7 +104,12 @@
|
|||||||
android:name=".service.WalletService"
|
android:name=".service.WalletService"
|
||||||
android:description="@string/service_description"
|
android:description="@string/service_description"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="Monero Wallet Service" />
|
android:foregroundServiceType="specialUse"
|
||||||
|
android:label="Monero Wallet Service">
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="Keeps app in sync with the blockchain" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2017 m2049r
|
* Copyright (c) 2017-2024 m2049r
|
||||||
* <p>
|
* <p>
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
@@ -28,6 +30,10 @@
|
|||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void ThrowException(JNIEnv *jenv, const char* type, const char* msg) {
|
||||||
|
jenv->ThrowNew(jenv->FindClass(type), msg);
|
||||||
|
}
|
||||||
|
|
||||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||||
jclass c = env->GetObjectClass(obj);
|
jclass c = env->GetObjectClass(obj);
|
||||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||||
@@ -35,8 +41,16 @@ jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handl
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||||
|
return reinterpret_cast<T *>(env->GetLongField(obj, getHandleField(env, obj, fieldName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void destroyNativeObject(JNIEnv *env, T nativeObjectHandle, jobject obj, const char *fieldName = "handle") {
|
||||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
||||||
return reinterpret_cast<T *>(handle);
|
if (handle != 0) {
|
||||||
|
ThrowException(env, "java/lang/IllegalStateException", "invalid handle (destroy)");
|
||||||
|
}
|
||||||
|
delete reinterpret_cast<T *>(nativeObjectHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
||||||
@@ -54,7 +68,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern const char* const MONERO_VERSION; // the actual monero core version
|
extern const char *const MONERO_VERSION; // the actual monero core version
|
||||||
|
|
||||||
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||||
enum {
|
enum {
|
||||||
@@ -62,18 +76,40 @@ enum {
|
|||||||
HASH_DATA_AREA = 136
|
HASH_DATA_AREA = 136
|
||||||
};
|
};
|
||||||
|
|
||||||
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height);
|
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed,
|
||||||
|
uint64_t height);
|
||||||
|
|
||||||
inline void slow_hash(const void *data, const size_t length, char *hash) {
|
inline void slow_hash(const void *data, const size_t length, char *hash) {
|
||||||
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
|
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
||||||
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/, 0 /*height*/);
|
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/,
|
||||||
|
0 /*height*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace Monerujo {
|
||||||
|
class SidekickWallet {
|
||||||
|
public:
|
||||||
|
enum Status {
|
||||||
|
Status_Ok,
|
||||||
|
Status_Error,
|
||||||
|
Status_Critical
|
||||||
|
};
|
||||||
|
|
||||||
|
SidekickWallet(uint8_t networkType, std::string a, std::string b);
|
||||||
|
|
||||||
|
~SidekickWallet();
|
||||||
|
|
||||||
|
std::string call(int commandId, const std::string &request);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
Status status() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
#endif //XMRWALLET_WALLET_LIB_H
|
#endif //XMRWALLET_WALLET_LIB_H
|
||||||
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
package com.btchip.comm;
|
package com.btchip.comm;
|
||||||
|
|
||||||
import com.btchip.BTChipException;
|
|
||||||
|
|
||||||
public interface BTChipTransport {
|
public interface BTChipTransport {
|
||||||
byte[] exchange(byte[] command);
|
byte[] exchange(byte[] command);
|
||||||
|
|
||||||
|
@@ -28,7 +28,6 @@ import android.hardware.usb.UsbInterface;
|
|||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.hardware.usb.UsbRequest;
|
import android.hardware.usb.UsbRequest;
|
||||||
|
|
||||||
import com.btchip.BTChipException;
|
|
||||||
import com.btchip.comm.BTChipTransport;
|
import com.btchip.comm.BTChipTransport;
|
||||||
import com.btchip.comm.LedgerHelper;
|
import com.btchip.comm.LedgerHelper;
|
||||||
import com.btchip.utils.Dump;
|
import com.btchip.utils.Dump;
|
||||||
|
File diff suppressed because it is too large
Load Diff
326
app/src/main/java/com/m2049r/xmrwallet/BluetoothFragment.java
Normal file
326
app/src/main/java/com/m2049r/xmrwallet/BluetoothFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,8 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
@@ -37,6 +34,7 @@ import android.view.inputmethod.EditorInfo;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -44,6 +42,7 @@ import androidx.fragment.app.Fragment;
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.util.FingerprintHelper;
|
import com.m2049r.xmrwallet.util.FingerprintHelper;
|
||||||
@@ -67,8 +66,20 @@ public class GenerateFragment extends Fragment {
|
|||||||
static final String TYPE_KEY = "key";
|
static final String TYPE_KEY = "key";
|
||||||
static final String TYPE_SEED = "seed";
|
static final String TYPE_SEED = "seed";
|
||||||
static final String TYPE_LEDGER = "ledger";
|
static final String TYPE_LEDGER = "ledger";
|
||||||
|
static final String TYPE_SIDEKICK = "sidekick";
|
||||||
static final String TYPE_VIEWONLY = "view";
|
static final String TYPE_VIEWONLY = "view";
|
||||||
|
|
||||||
|
static Wallet.Device getDeviceType(String type) {
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_SIDEKICK:
|
||||||
|
return Wallet.Device.Sidekick;
|
||||||
|
case TYPE_LEDGER:
|
||||||
|
return Wallet.Device.Ledger;
|
||||||
|
default:
|
||||||
|
return Wallet.Device.Software;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private TextInputLayout etWalletName;
|
private TextInputLayout etWalletName;
|
||||||
private PasswordEntryView etWalletPassword;
|
private PasswordEntryView etWalletPassword;
|
||||||
private LinearLayout llFingerprintAuth;
|
private LinearLayout llFingerprintAuth;
|
||||||
@@ -195,6 +206,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_UNSPECIFIED);
|
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_UNSPECIFIED);
|
||||||
break;
|
break;
|
||||||
case TYPE_LEDGER:
|
case TYPE_LEDGER:
|
||||||
|
case TYPE_SIDEKICK:
|
||||||
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
|
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
etWalletPassword.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
etWalletPassword.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||||
@@ -308,7 +320,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
private boolean checkName() {
|
private boolean checkName() {
|
||||||
String name = etWalletName.getEditText().getText().toString();
|
String name = etWalletName.getEditText().getText().toString();
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
if (name.length() == 0) {
|
if (name.isEmpty()) {
|
||||||
etWalletName.setError(getString(R.string.generate_wallet_name));
|
etWalletName.setError(getString(R.string.generate_wallet_name));
|
||||||
ok = false;
|
ok = false;
|
||||||
} else if (name.charAt(0) == '.') {
|
} else if (name.charAt(0) == '.') {
|
||||||
@@ -345,21 +357,23 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
|
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
|
||||||
if (restoreHeight.isEmpty()) return -1;
|
if (restoreHeight.isEmpty()) return -1;
|
||||||
try {
|
if (WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) {
|
||||||
// is it a date?
|
|
||||||
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
|
|
||||||
parser.setLenient(false);
|
|
||||||
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
|
||||||
} catch (ParseException ignored) {
|
|
||||||
}
|
|
||||||
if ((height < 0) && (restoreHeight.length() == 8))
|
|
||||||
try {
|
try {
|
||||||
// is it a date without dashes?
|
// is it a date?
|
||||||
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
|
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
parser.setLenient(false);
|
parser.setLenient(false);
|
||||||
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
||||||
} catch (ParseException ignored) {
|
} catch (ParseException ignored) {
|
||||||
}
|
}
|
||||||
|
if ((height < 0) && (restoreHeight.length() == 8))
|
||||||
|
try {
|
||||||
|
// is it a date without dashes?
|
||||||
|
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
parser.setLenient(false);
|
||||||
|
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
||||||
|
} catch (ParseException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
if (height < 0)
|
if (height < 0)
|
||||||
try {
|
try {
|
||||||
// or is it a height?
|
// or is it a height?
|
||||||
@@ -448,11 +462,12 @@ public class GenerateFragment extends Fragment {
|
|||||||
activityCallback.onGenerate(name, crazyPass, seed, offset, height);
|
activityCallback.onGenerate(name, crazyPass, seed, offset, height);
|
||||||
break;
|
break;
|
||||||
case TYPE_LEDGER:
|
case TYPE_LEDGER:
|
||||||
|
case TYPE_SIDEKICK:
|
||||||
bGenerate.setEnabled(false);
|
bGenerate.setEnabled(false);
|
||||||
if (fingerprintAuthAllowed) {
|
if (fingerprintAuthAllowed) {
|
||||||
KeyStoreHelper.saveWalletUserPass(requireActivity(), name, password);
|
KeyStoreHelper.saveWalletUserPass(requireActivity(), name, password);
|
||||||
}
|
}
|
||||||
activityCallback.onGenerateLedger(name, crazyPass, height);
|
activityCallback.onGenerateDevice(getDeviceType(type), name, crazyPass, height);
|
||||||
break;
|
break;
|
||||||
case TYPE_KEY:
|
case TYPE_KEY:
|
||||||
case TYPE_VIEWONLY:
|
case TYPE_VIEWONLY:
|
||||||
@@ -496,6 +511,8 @@ public class GenerateFragment extends Fragment {
|
|||||||
return getString(R.string.generate_wallet_type_seed);
|
return getString(R.string.generate_wallet_type_seed);
|
||||||
case TYPE_LEDGER:
|
case TYPE_LEDGER:
|
||||||
return getString(R.string.generate_wallet_type_ledger);
|
return getString(R.string.generate_wallet_type_ledger);
|
||||||
|
case TYPE_SIDEKICK:
|
||||||
|
return getString(R.string.generate_wallet_type_sidekick);
|
||||||
case TYPE_VIEWONLY:
|
case TYPE_VIEWONLY:
|
||||||
return getString(R.string.generate_wallet_type_view);
|
return getString(R.string.generate_wallet_type_view);
|
||||||
default:
|
default:
|
||||||
@@ -513,7 +530,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
|
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
|
||||||
|
|
||||||
void onGenerateLedger(String name, String password, long height);
|
void onGenerateDevice(Wallet.Device device, String name, String password, long height);
|
||||||
|
|
||||||
void setTitle(String title);
|
void setTitle(String title);
|
||||||
|
|
||||||
@@ -553,6 +570,9 @@ public class GenerateFragment extends Fragment {
|
|||||||
case TYPE_LEDGER:
|
case TYPE_LEDGER:
|
||||||
inflater.inflate(R.menu.create_wallet_ledger, menu);
|
inflater.inflate(R.menu.create_wallet_ledger, menu);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_SIDEKICK:
|
||||||
|
inflater.inflate(R.menu.create_wallet_sidekick, menu);
|
||||||
|
break;
|
||||||
case TYPE_VIEWONLY:
|
case TYPE_VIEWONLY:
|
||||||
inflater.inflate(R.menu.create_wallet_view, menu);
|
inflater.inflate(R.menu.create_wallet_view, menu);
|
||||||
break;
|
break;
|
||||||
@@ -579,13 +599,11 @@ public class GenerateFragment extends Fragment {
|
|||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(getString(R.string.label_ok), null)
|
.setPositiveButton(getString(R.string.label_ok), null)
|
||||||
.setNegativeButton(getString(R.string.label_cancel),
|
.setNegativeButton(getString(R.string.label_cancel),
|
||||||
new DialogInterface.OnClickListener() {
|
(dialog, id) -> {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
Helper.hideKeyboardAlways(activity);
|
||||||
Helper.hideKeyboardAlways(activity);
|
etWalletMnemonic.getEditText().getText().clear();
|
||||||
etWalletMnemonic.getEditText().getText().clear();
|
dialog.cancel();
|
||||||
dialog.cancel();
|
ledgerDialog = null;
|
||||||
ledgerDialog = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ledgerDialog = alertDialogBuilder.create();
|
ledgerDialog = alertDialogBuilder.create();
|
||||||
|
@@ -40,6 +40,7 @@ import android.widget.ScrollView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
@@ -98,6 +99,13 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
private String walletPath;
|
private String walletPath;
|
||||||
private String walletName;
|
private String walletName;
|
||||||
|
|
||||||
|
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
@@ -156,10 +164,14 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
|
assert args != null;
|
||||||
type = args.getString(REQUEST_TYPE);
|
type = args.getString(REQUEST_TYPE);
|
||||||
walletPath = args.getString(REQUEST_PATH);
|
walletPath = args.getString(REQUEST_PATH);
|
||||||
localPassword = args.getString(REQUEST_PASSWORD);
|
localPassword = args.getString(REQUEST_PASSWORD);
|
||||||
showDetails();
|
showDetails();
|
||||||
|
if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) {
|
||||||
|
backPressedCallback.setEnabled(true);
|
||||||
|
}
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +255,7 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
showProgress();
|
showProgress();
|
||||||
if ((walletPath != null)
|
if ((walletPath != null)
|
||||||
&& (WalletManager.getInstance().queryWalletDevice(walletPath + ".keys", getPassword())
|
&& (WalletManager.getInstance().queryWalletDevice(walletPath + ".keys", getPassword())
|
||||||
== Wallet.Device.Device_Ledger)
|
== Wallet.Device.Ledger)
|
||||||
&& (progressCallback != null)) {
|
&& (progressCallback != null)) {
|
||||||
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
||||||
dialogOpened = true;
|
dialogOpened = true;
|
||||||
@@ -275,10 +287,11 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
height = wallet.getRestoreHeight();
|
height = wallet.getRestoreHeight();
|
||||||
seed = wallet.getSeed(getSeedOffset());
|
seed = wallet.getSeed(getSeedOffset());
|
||||||
switch (wallet.getDeviceType()) {
|
switch (wallet.getDeviceType()) {
|
||||||
case Device_Ledger:
|
case Ledger:
|
||||||
viewKey = Ledger.Key();
|
viewKey = Ledger.Key();
|
||||||
break;
|
break;
|
||||||
case Device_Software:
|
case Software:
|
||||||
|
case Sidekick:
|
||||||
viewKey = wallet.getSecretViewKey();
|
viewKey = wallet.getSecretViewKey();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -420,14 +433,11 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
pbProgress.setVisibility(View.INVISIBLE);
|
pbProgress.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean backOk() {
|
|
||||||
return !type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
38
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
38
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class LockFragment extends Fragment {
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
Timber.d("onCreateView");
|
||||||
|
final FrameLayout frame = new FrameLayout(requireContext());
|
||||||
|
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -29,13 +29,13 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -46,6 +46,7 @@ import com.google.android.material.progressindicator.CircularProgressIndicator;
|
|||||||
import com.m2049r.xmrwallet.data.NodeInfo;
|
import com.m2049r.xmrwallet.data.NodeInfo;
|
||||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||||
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
|
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||||
@@ -61,6 +62,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInteractionListener,
|
public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInteractionListener,
|
||||||
@@ -115,7 +117,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
|
|
||||||
Set<NodeInfo> getOrPopulateFavourites();
|
Set<NodeInfo> getOrPopulateFavourites();
|
||||||
|
|
||||||
boolean hasLedger();
|
boolean hasDevice(Wallet.Device type);
|
||||||
|
|
||||||
|
boolean isNetworkAvailable();
|
||||||
|
|
||||||
void runOnNetCipher(Runnable runnable);
|
void runOnNetCipher(Runnable runnable);
|
||||||
}
|
}
|
||||||
@@ -146,9 +150,15 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_SETTINGS);
|
activityCallback.setToolbarButton(Toolbar.BUTTON_SETTINGS);
|
||||||
activityCallback.showNet();
|
activityCallback.showNet();
|
||||||
showNetwork();
|
showNetwork();
|
||||||
//activityCallback.runOnNetCipher(this::pingSelectedNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
animateFAB();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
@@ -165,6 +175,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
fabSeed = view.findViewById(R.id.fabSeed);
|
fabSeed = view.findViewById(R.id.fabSeed);
|
||||||
fabImport = view.findViewById(R.id.fabImport);
|
fabImport = view.findViewById(R.id.fabImport);
|
||||||
fabLedger = view.findViewById(R.id.fabLedger);
|
fabLedger = view.findViewById(R.id.fabLedger);
|
||||||
|
fabSidekick = view.findViewById(R.id.fabSidekick);
|
||||||
|
|
||||||
fabNewL = view.findViewById(R.id.fabNewL);
|
fabNewL = view.findViewById(R.id.fabNewL);
|
||||||
fabViewL = view.findViewById(R.id.fabViewL);
|
fabViewL = view.findViewById(R.id.fabViewL);
|
||||||
@@ -172,6 +183,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
fabSeedL = view.findViewById(R.id.fabSeedL);
|
fabSeedL = view.findViewById(R.id.fabSeedL);
|
||||||
fabImportL = view.findViewById(R.id.fabImportL);
|
fabImportL = view.findViewById(R.id.fabImportL);
|
||||||
fabLedgerL = view.findViewById(R.id.fabLedgerL);
|
fabLedgerL = view.findViewById(R.id.fabLedgerL);
|
||||||
|
fabSidekickL = view.findViewById(R.id.fabSidekickL);
|
||||||
|
|
||||||
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
|
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
|
||||||
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
|
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
|
||||||
@@ -187,6 +199,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
fabSeed.setOnClickListener(this);
|
fabSeed.setOnClickListener(this);
|
||||||
fabImport.setOnClickListener(this);
|
fabImport.setOnClickListener(this);
|
||||||
fabLedger.setOnClickListener(this);
|
fabLedger.setOnClickListener(this);
|
||||||
|
fabSidekick.setOnClickListener(this);
|
||||||
fabScreen.setOnClickListener(this);
|
fabScreen.setOnClickListener(this);
|
||||||
|
|
||||||
RecyclerView recyclerView = view.findViewById(R.id.list);
|
RecyclerView recyclerView = view.findViewById(R.id.list);
|
||||||
@@ -195,6 +208,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
ViewGroup llNotice = view.findViewById(R.id.llNotice);
|
ViewGroup llNotice = view.findViewById(R.id.llNotice);
|
||||||
|
|
||||||
Notice.showAll(llNotice, ".*_login");
|
Notice.showAll(llNotice, ".*_login");
|
||||||
|
|
||||||
view.findViewById(R.id.llNode).setOnClickListener(v -> startNodePrefs());
|
view.findViewById(R.id.llNode).setOnClickListener(v -> startNodePrefs());
|
||||||
@@ -269,7 +283,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove information of non-existent wallet
|
// remove information of non-existent wallet
|
||||||
Set<String> removedWallets = getActivity()
|
Set<String> removedWallets = requireActivity()
|
||||||
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
|
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
.getAll().keySet();
|
.getAll().keySet();
|
||||||
for (WalletManager.WalletInfo s : walletList) {
|
for (WalletManager.WalletInfo s : walletList) {
|
||||||
@@ -288,6 +302,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -296,25 +311,30 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFabOpen = false;
|
@Getter
|
||||||
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabImport, fabLedger;
|
private boolean fabOpen = false;
|
||||||
|
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabImport, fabLedger, fabSidekick;
|
||||||
private RelativeLayout fabScreen;
|
private RelativeLayout fabScreen;
|
||||||
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabImportL, fabLedgerL;
|
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabImportL, fabLedgerL, fabSidekickL;
|
||||||
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
|
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
|
||||||
private Animation fab_pulse;
|
private Animation fab_pulse;
|
||||||
|
|
||||||
public boolean isFabOpen() {
|
private void setFabOpen(boolean value) {
|
||||||
return isFabOpen;
|
fabOpen = value;
|
||||||
|
onBackPressedCallback.setEnabled(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void animateFAB() {
|
public void animateFAB() {
|
||||||
if (isFabOpen) { // close the fab
|
if (isFabOpen()) { // close the fab
|
||||||
fabScreen.setClickable(false);
|
fabScreen.setClickable(false);
|
||||||
fabScreen.startAnimation(fab_close_screen);
|
fabScreen.startAnimation(fab_close_screen);
|
||||||
fab.startAnimation(rotate_backward);
|
fab.startAnimation(rotate_backward);
|
||||||
if (fabLedgerL.getVisibility() == View.VISIBLE) {
|
if (fabLedgerL.getVisibility() == View.VISIBLE) {
|
||||||
fabLedgerL.startAnimation(fab_close);
|
fabLedgerL.startAnimation(fab_close);
|
||||||
fabLedger.setClickable(false);
|
fabLedger.setClickable(false);
|
||||||
|
} else if (fabSidekickL.getVisibility() == View.VISIBLE) {
|
||||||
|
fabSidekickL.startAnimation(fab_close);
|
||||||
|
fabSidekick.setClickable(false);
|
||||||
} else {
|
} else {
|
||||||
fabNewL.startAnimation(fab_close);
|
fabNewL.startAnimation(fab_close);
|
||||||
fabNew.setClickable(false);
|
fabNew.setClickable(false);
|
||||||
@@ -327,22 +347,30 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
fabImportL.startAnimation(fab_close);
|
fabImportL.startAnimation(fab_close);
|
||||||
fabImport.setClickable(false);
|
fabImport.setClickable(false);
|
||||||
}
|
}
|
||||||
isFabOpen = false;
|
setFabOpen(false);
|
||||||
} else { // open the fab
|
} else { // open the fab
|
||||||
fabScreen.setClickable(true);
|
fabScreen.setClickable(true);
|
||||||
fabScreen.startAnimation(fab_open_screen);
|
fabScreen.startAnimation(fab_open_screen);
|
||||||
fab.startAnimation(rotate_forward);
|
fab.startAnimation(rotate_forward);
|
||||||
if (activityCallback.hasLedger()) {
|
if ((activityCallback.hasDevice(Wallet.Device.Ledger)
|
||||||
fabLedgerL.setVisibility(View.VISIBLE);
|
|| activityCallback.hasDevice(Wallet.Device.Sidekick))) {
|
||||||
fabNewL.setVisibility(View.GONE);
|
fabNewL.setVisibility(View.GONE);
|
||||||
fabViewL.setVisibility(View.GONE);
|
fabViewL.setVisibility(View.GONE);
|
||||||
fabKeyL.setVisibility(View.GONE);
|
fabKeyL.setVisibility(View.GONE);
|
||||||
fabSeedL.setVisibility(View.GONE);
|
fabSeedL.setVisibility(View.GONE);
|
||||||
fabImportL.setVisibility(View.GONE);
|
fabImportL.setVisibility(View.GONE);
|
||||||
|
|
||||||
fabLedgerL.startAnimation(fab_open);
|
if (activityCallback.hasDevice(Wallet.Device.Ledger)) {
|
||||||
fabLedger.setClickable(true);
|
fabLedgerL.setVisibility(View.VISIBLE);
|
||||||
|
fabLedgerL.startAnimation(fab_open);
|
||||||
|
fabLedger.setClickable(true);
|
||||||
|
} else { // Sidekick
|
||||||
|
fabSidekickL.setVisibility(View.VISIBLE);
|
||||||
|
fabSidekickL.startAnimation(fab_open);
|
||||||
|
fabSidekick.setClickable(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
fabSidekickL.setVisibility(View.GONE);
|
||||||
fabLedgerL.setVisibility(View.GONE);
|
fabLedgerL.setVisibility(View.GONE);
|
||||||
fabNewL.setVisibility(View.VISIBLE);
|
fabNewL.setVisibility(View.VISIBLE);
|
||||||
fabViewL.setVisibility(View.VISIBLE);
|
fabViewL.setVisibility(View.VISIBLE);
|
||||||
@@ -361,7 +389,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
fabImportL.startAnimation(fab_open);
|
fabImportL.startAnimation(fab_open);
|
||||||
fabImport.setClickable(true);
|
fabImport.setClickable(true);
|
||||||
}
|
}
|
||||||
isFabOpen = true;
|
setFabOpen(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +401,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
animateFAB();
|
animateFAB();
|
||||||
} else if (id == R.id.fabNew) {
|
} else if (id == R.id.fabNew) {
|
||||||
fabScreen.setVisibility(View.INVISIBLE);
|
fabScreen.setVisibility(View.INVISIBLE);
|
||||||
isFabOpen = false;
|
setFabOpen(false);
|
||||||
activityCallback.onAddWallet(GenerateFragment.TYPE_NEW);
|
activityCallback.onAddWallet(GenerateFragment.TYPE_NEW);
|
||||||
} else if (id == R.id.fabView) {
|
} else if (id == R.id.fabView) {
|
||||||
animateFAB();
|
animateFAB();
|
||||||
@@ -391,6 +419,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
Timber.d("FAB_LEDGER");
|
Timber.d("FAB_LEDGER");
|
||||||
animateFAB();
|
animateFAB();
|
||||||
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
|
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
|
||||||
|
} else if (id == R.id.fabSidekick) {
|
||||||
|
Timber.d("FAB_SIDEKICK");
|
||||||
|
animateFAB();
|
||||||
|
activityCallback.onAddWallet(GenerateFragment.TYPE_SIDEKICK);
|
||||||
} else if (id == R.id.fabScreen) {
|
} else if (id == R.id.fabScreen) {
|
||||||
animateFAB();
|
animateFAB();
|
||||||
}
|
}
|
||||||
@@ -413,7 +445,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setSubtext(String status) {
|
private void setSubtext(String status) {
|
||||||
final Context ctx = getContext();
|
final Context ctx = requireContext();
|
||||||
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
@@ -20,7 +20,6 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
|
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
|
||||||
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
|
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
|
||||||
|
@@ -31,6 +31,8 @@ import android.widget.Button;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -67,7 +69,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
static private int NODES_TO_FIND = 10;
|
static private int NODES_TO_FIND = 10;
|
||||||
|
|
||||||
static private NumberFormat FORMATTER = NumberFormat.getInstance();
|
static private final NumberFormat FORMATTER = NumberFormat.getInstance();
|
||||||
|
|
||||||
private SwipeRefreshLayout pullToRefresh;
|
private SwipeRefreshLayout pullToRefresh;
|
||||||
private TextView tvPull;
|
private TextView tvPull;
|
||||||
@@ -103,7 +105,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof Listener) {
|
if (context instanceof Listener) {
|
||||||
this.activityCallback = (Listener) context;
|
this.activityCallback = (Listener) context;
|
||||||
@@ -145,6 +147,13 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
Toast.makeText(requireActivity(), getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
@@ -187,6 +196,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private boolean refresh(int type) {
|
private boolean refresh(int type) {
|
||||||
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
|
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
asyncFindNodes = new AsyncFindNodes();
|
asyncFindNodes = new AsyncFindNodes();
|
||||||
updateRefreshElements();
|
updateRefreshElements();
|
||||||
asyncFindNodes.execute(type);
|
asyncFindNodes.execute(type);
|
||||||
@@ -197,10 +207,11 @@ public class NodeFragment extends Fragment
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.node_menu, menu);
|
inflater.inflate(R.menu.node_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
@@ -279,8 +290,7 @@ public class NodeFragment extends Fragment
|
|||||||
} else if (params[0] == SCAN) {
|
} else if (params[0] == SCAN) {
|
||||||
// otherwise scan the network
|
// otherwise scan the network
|
||||||
Timber.d("scanning");
|
Timber.d("scanning");
|
||||||
Set<NodeInfo> seedList = new HashSet<>();
|
Set<NodeInfo> seedList = new HashSet<>(nodeList);
|
||||||
seedList.addAll(nodeList);
|
|
||||||
nodeList.clear();
|
nodeList.clear();
|
||||||
Timber.d("seed %d", seedList.size());
|
Timber.d("seed %d", seedList.size());
|
||||||
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
||||||
@@ -342,6 +352,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private void complete() {
|
private void complete() {
|
||||||
asyncFindNodes = null;
|
asyncFindNodes = null;
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
//if (isCancelled()) return;
|
//if (isCancelled()) return;
|
||||||
tvPull.setText(getString(R.string.node_pull_hint));
|
tvPull.setText(getString(R.string.node_pull_hint));
|
||||||
@@ -444,7 +455,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private void closeDialog() {
|
private void closeDialog() {
|
||||||
if (editDialog == null) throw new IllegalStateException();
|
if (editDialog == null) throw new IllegalStateException();
|
||||||
Helper.hideKeyboardAlways(getActivity());
|
Helper.hideKeyboardAlways(requireActivity());
|
||||||
editDialog.dismiss();
|
editDialog.dismiss();
|
||||||
editDialog = null;
|
editDialog = null;
|
||||||
NodeFragment.this.editDialog = null;
|
NodeFragment.this.editDialog = null;
|
||||||
@@ -575,6 +586,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreDefaultNodes() {
|
void restoreDefaultNodes() {
|
||||||
|
@@ -22,7 +22,6 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.nfc.NfcManager;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
@@ -33,12 +32,10 @@ import android.view.KeyEvent;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -85,8 +82,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private ImageView ivQrCode;
|
private ImageView ivQrCode;
|
||||||
private ImageView ivQrCodeFull;
|
private ImageView ivQrCodeFull;
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
private ImageButton bCopyAddress;
|
|
||||||
private MenuItem shareItem;
|
|
||||||
|
|
||||||
private Wallet wallet = null;
|
private Wallet wallet = null;
|
||||||
private boolean isMyWallet = false;
|
private boolean isMyWallet = false;
|
||||||
@@ -117,11 +112,10 @@ public class ReceiveFragment extends Fragment {
|
|||||||
tvQrCode = view.findViewById(R.id.tvQrCode);
|
tvQrCode = view.findViewById(R.id.tvQrCode);
|
||||||
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
||||||
etDummy = view.findViewById(R.id.etDummy);
|
etDummy = view.findViewById(R.id.etDummy);
|
||||||
bCopyAddress = view.findViewById(R.id.bCopyAddress);
|
|
||||||
|
|
||||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
|
|
||||||
bCopyAddress.setOnClickListener(v -> copyAddress());
|
view.findViewById(R.id.bCopyAddress).setOnClickListener(v -> copyAddress());
|
||||||
|
|
||||||
evAmount.setOnNewAmountListener(xmr -> {
|
evAmount.setOnNewAmountListener(xmr -> {
|
||||||
Timber.d("new amount = %s", xmr);
|
Timber.d("new amount = %s", xmr);
|
||||||
@@ -191,11 +185,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
throw new IllegalStateException("no wallet info");
|
throw new IllegalStateException("no wallet info");
|
||||||
}
|
}
|
||||||
|
|
||||||
View tvNfc = view.findViewById(R.id.tvNfc);
|
|
||||||
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
|
|
||||||
if ((manager != null) && (manager.getDefaultAdapter() != null))
|
|
||||||
tvNfc.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,8 +206,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
inflater.inflate(R.menu.receive_menu, menu);
|
inflater.inflate(R.menu.receive_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
|
||||||
shareItem = menu.findItem(R.id.menu_item_share);
|
menu.findItem(R.id.menu_item_share).setOnMenuItemClickListener(item -> {
|
||||||
shareItem.setOnMenuItemClickListener(item -> {
|
|
||||||
if (shareRequested) return true;
|
if (shareRequested) return true;
|
||||||
shareRequested = true;
|
shareRequested = true;
|
||||||
if (!qrValid) {
|
if (!qrValid) {
|
||||||
@@ -244,7 +232,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private boolean saveQrCode() {
|
private boolean saveQrCode() {
|
||||||
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
||||||
|
|
||||||
File cachePath = new File(getActivity().getCacheDir(), "images");
|
File cachePath = new File(requireActivity().getCacheDir(), "images");
|
||||||
if (!cachePath.exists())
|
if (!cachePath.exists())
|
||||||
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
||||||
File png = new File(cachePath, "QR.png");
|
File png = new File(cachePath, "QR.png");
|
||||||
@@ -403,7 +391,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
|
|
||||||
private Bitmap getMoneroLogo() {
|
private Bitmap getMoneroLogo() {
|
||||||
if (logo == null) {
|
if (logo == null) {
|
||||||
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_logo_b);
|
logo = Helper.getBitmap(getContext(), R.drawable.ic_monerujo_qr);
|
||||||
}
|
}
|
||||||
return logo;
|
return logo;
|
||||||
}
|
}
|
||||||
@@ -458,7 +446,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
.withEndAction(resetSize).start();
|
.withEndAction(resetSize).start();
|
||||||
}
|
}
|
||||||
subaddress = newSubaddress;
|
subaddress = newSubaddress;
|
||||||
final Context context = getContext();
|
final Context context = requireContext();
|
||||||
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
@@ -19,12 +19,14 @@ package com.m2049r.xmrwallet;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
private ZXingScannerView mScannerView;
|
private ZXingScannerView mScannerView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Timber.d("onCreateView");
|
Timber.d("onCreateView");
|
||||||
mScannerView = new ZXingScannerView(getActivity());
|
mScannerView = new ZXingScannerView(getActivity());
|
||||||
return mScannerView;
|
return mScannerView;
|
||||||
@@ -84,7 +86,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof OnScannedListener) {
|
if (context instanceof OnScannedListener) {
|
||||||
this.onScannedListener = (OnScannedListener) context;
|
this.onScannedListener = (OnScannedListener) context;
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import static android.view.WindowManager.LayoutParams;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -29,8 +31,6 @@ import com.m2049r.xmrwallet.util.LocaleHelper;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static android.view.WindowManager.LayoutParams;
|
|
||||||
|
|
||||||
public abstract class SecureActivity extends AppCompatActivity {
|
public abstract class SecureActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StyleRes;
|
import androidx.annotation.StyleRes;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
@@ -60,7 +61,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private SettingsFragment.Listener activity;
|
private SettingsFragment.Listener activity;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof SettingsFragment.Listener) {
|
if (context instanceof SettingsFragment.Listener) {
|
||||||
activity = (SettingsFragment.Listener) context;
|
activity = (SettingsFragment.Listener) context;
|
||||||
|
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.data.BluetoothInfo;
|
||||||
|
import com.m2049r.xmrwallet.layout.BluetoothInfoAdapter;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class SidekickConnectFragment extends Fragment
|
||||||
|
implements BluetoothInfoAdapter.OnInteractionListener {
|
||||||
|
|
||||||
|
private BluetoothAdapter bluetoothAdapter;
|
||||||
|
|
||||||
|
private SwipeRefreshLayout pullToRefresh;
|
||||||
|
|
||||||
|
private BluetoothInfoAdapter infoAdapter;
|
||||||
|
|
||||||
|
private Listener activityCallback;
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void setToolbarButton(int type);
|
||||||
|
|
||||||
|
void setSubtitle(String title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof Listener) {
|
||||||
|
this.activityCallback = (Listener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException(context + " must implement Listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
Timber.d("onPause()");
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
if (bluetoothAdapter.isDiscovering()) {
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Timber.d("onResume()");
|
||||||
|
activityCallback.setSubtitle(getString(R.string.label_bluetooth));
|
||||||
|
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
|
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
||||||
|
assert btFragment != null;
|
||||||
|
btFragment.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
Timber.d("onCreateView");
|
||||||
|
View view = inflater.inflate(R.layout.fragment_sidekick_connect, container, false);
|
||||||
|
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.list);
|
||||||
|
infoAdapter = new BluetoothInfoAdapter(this);
|
||||||
|
recyclerView.setAdapter(infoAdapter);
|
||||||
|
|
||||||
|
pullToRefresh = view.findViewById(R.id.pullToRefresh);
|
||||||
|
pullToRefresh.setOnRefreshListener(() -> {
|
||||||
|
populateList();
|
||||||
|
pullToRefresh.setRefreshing(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateList() {
|
||||||
|
List<BluetoothInfo> items = new ArrayList<>();
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
|
||||||
|
final int deviceCLass = device.getBluetoothClass().getDeviceClass();
|
||||||
|
switch (deviceCLass) {
|
||||||
|
case BluetoothClass.Device.PHONE_SMART:
|
||||||
|
//TODO verify these are correct
|
||||||
|
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
|
||||||
|
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
|
||||||
|
items.add(new BluetoothInfo(device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
infoAdapter.setItems(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
Helper.hideKeyboard(getActivity());
|
||||||
|
|
||||||
|
// Get the local Bluetooth adapter
|
||||||
|
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
populateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.sidekick_connect_menu, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
// Make sure we're not doing discovery anymore
|
||||||
|
if (bluetoothAdapter != null) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInteraction(final View view, final BluetoothInfo item) {
|
||||||
|
Timber.d("onInteraction %s", item);
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
throw new IllegalStateException("Bluetooth permission not granted");
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
|
||||||
|
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
||||||
|
assert btFragment != null;
|
||||||
|
btFragment.connectDevice(item.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void allowClick() {
|
||||||
|
infoAdapter.allowClick(true);
|
||||||
|
}
|
||||||
|
}
|
@@ -195,7 +195,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
if ((wallet.getDeviceType() == Wallet.Device.Device_Ledger) && (progressCallback != null)) {
|
if ((wallet.getDeviceType() == Wallet.Device.Ledger) && (progressCallback != null)) {
|
||||||
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
||||||
dialogOpened = true;
|
dialogOpened = true;
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
|
|
||||||
// Callbacks from SubaddressInfoAdapter
|
// Callbacks from SubaddressInfoAdapter
|
||||||
@Override
|
@Override
|
||||||
public void onInteraction(final View view, final Subaddress subaddress) {
|
public void onInteraction(final View view, @NonNull final Subaddress subaddress) {
|
||||||
if (managerMode)
|
if (managerMode)
|
||||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
else
|
else
|
||||||
@@ -238,4 +238,5 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -46,13 +46,12 @@ import java.util.List;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class SubaddressInfoFragment extends Fragment
|
public class SubaddressInfoFragment extends Fragment
|
||||||
implements TransactionInfoAdapter.OnInteractionListener, OnBlockUpdateListener {
|
implements TransactionInfoAdapter.Listener, OnBlockUpdateListener {
|
||||||
private TransactionInfoAdapter adapter;
|
private TransactionInfoAdapter adapter;
|
||||||
|
|
||||||
private Subaddress subaddress;
|
private Subaddress subaddress;
|
||||||
|
|
||||||
private TextInputLayout etName;
|
private TextInputLayout etName;
|
||||||
private TextView tvAddress;
|
|
||||||
private TextView tvTxLabel;
|
private TextView tvTxLabel;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +60,6 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
||||||
|
|
||||||
etName = view.findViewById(R.id.etName);
|
etName = view.findViewById(R.id.etName);
|
||||||
tvAddress = view.findViewById(R.id.tvAddress);
|
|
||||||
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
||||||
|
|
||||||
final RecyclerView list = view.findViewById(R.id.list);
|
final RecyclerView list = view.findViewById(R.id.list);
|
||||||
@@ -71,12 +69,14 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
final Wallet wallet = activityCallback.getWallet();
|
final Wallet wallet = activityCallback.getWallet();
|
||||||
|
|
||||||
Bundle b = getArguments();
|
Bundle b = getArguments();
|
||||||
|
assert b != null;
|
||||||
final int subaddressIndex = b.getInt("subaddressIndex");
|
final int subaddressIndex = b.getInt("subaddressIndex");
|
||||||
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
||||||
|
|
||||||
etName.getEditText().setText(subaddress.getDisplayLabel());
|
etName.getEditText().setText(subaddress.getDisplayLabel());
|
||||||
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
|
final TextView tvAddress = view.findViewById(R.id.tvAddress);
|
||||||
subaddress.getAddressIndex(), subaddress.getSquashedAddress()));
|
tvAddress.setText(requireContext().getString(R.string.subbaddress_info_subtitle,
|
||||||
|
subaddress.getAddressIndex(), subaddress.getAddress()));
|
||||||
|
|
||||||
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
if (!hasFocus) {
|
if (!hasFocus) {
|
||||||
@@ -145,6 +145,8 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
void setTitle(String title, String subtitle);
|
void setTitle(String title, String subtitle);
|
||||||
|
|
||||||
void setSubtitle(String subtitle);
|
void setSubtitle(String subtitle);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -170,4 +172,9 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDaemonHeight() {
|
||||||
|
return activityCallback.getDaemonHeight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
@@ -34,6 +33,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.transition.Transition;
|
import androidx.transition.Transition;
|
||||||
@@ -44,6 +44,8 @@ import com.m2049r.xmrwallet.data.UserNotes;
|
|||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.model.Transfer;
|
import com.m2049r.xmrwallet.model.Transfer;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftService;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.api.ShiftApi;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
@@ -61,6 +63,7 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
static public final String ARG_INFO = "info";
|
static public final String ARG_INFO = "info";
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
||||||
|
|
||||||
public TxFragment() {
|
public TxFragment() {
|
||||||
@@ -79,10 +82,14 @@ public class TxFragment extends Fragment {
|
|||||||
private TextView tvTxPaymentId;
|
private TextView tvTxPaymentId;
|
||||||
private TextView tvTxBlockheight;
|
private TextView tvTxBlockheight;
|
||||||
private TextView tvTxAmount;
|
private TextView tvTxAmount;
|
||||||
|
private TextView tvTxPocketChangeAmount;
|
||||||
private TextView tvTxFee;
|
private TextView tvTxFee;
|
||||||
private TextView tvTxTransfers;
|
private TextView tvTxTransfers;
|
||||||
private TextView etTxNotes;
|
private TextView etTxNotes;
|
||||||
|
|
||||||
|
private View llWarning;
|
||||||
|
private TextView tvWarning;
|
||||||
|
|
||||||
// XMRTO stuff
|
// XMRTO stuff
|
||||||
private View cvXmrTo;
|
private View cvXmrTo;
|
||||||
private TextView tvTxXmrToKey;
|
private TextView tvTxXmrToKey;
|
||||||
@@ -102,6 +109,7 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
||||||
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||||
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
||||||
|
|
||||||
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
||||||
|
|
||||||
tvAccount = view.findViewById(R.id.tvAccount);
|
tvAccount = view.findViewById(R.id.tvAccount);
|
||||||
@@ -113,25 +121,30 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
||||||
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
|
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
|
||||||
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
||||||
|
tvTxPocketChangeAmount = view.findViewById(R.id.tvTxPocketChangeAmount);
|
||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
||||||
etTxNotes = view.findViewById(R.id.etTxNotes);
|
etTxNotes = view.findViewById(R.id.etTxNotes);
|
||||||
|
|
||||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
|
||||||
|
llWarning = view.findViewById(R.id.llWarning);
|
||||||
|
tvWarning = view.findViewById(R.id.tvWarning);
|
||||||
|
|
||||||
tvTxXmrToKey.setOnClickListener(v -> {
|
tvTxXmrToKey.setOnClickListener(v -> {
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
info = getArguments().getParcelable(ARG_INFO);
|
final Bundle args = getArguments();
|
||||||
|
assert args != null;
|
||||||
|
info = args.getParcelable(ARG_INFO);
|
||||||
show();
|
show();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shareTxInfo() {
|
void shareTxInfo() {
|
||||||
if (this.info == null) return;
|
if (this.info == null) return;
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
||||||
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
||||||
@@ -209,7 +222,7 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
private void showSubaddressLabel() {
|
private void showSubaddressLabel() {
|
||||||
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
||||||
final Context ctx = getContext();
|
final Context ctx = requireContext();
|
||||||
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
||||||
info.accountIndex, info.addressIndex,
|
info.accountIndex, info.addressIndex,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
@@ -244,8 +257,10 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||||
|
|
||||||
long realAmount = info.amount;
|
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
|
||||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
|
final long pcAmount = info.getPocketChangeAmount();
|
||||||
|
tvTxPocketChangeAmount.setVisibility(pcAmount > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
tvTxPocketChangeAmount.setText(getString(R.string.pocketchange_tx_detail, Wallet.getDisplayAmount(pcAmount)));
|
||||||
|
|
||||||
if ((info.fee > 0)) {
|
if ((info.fee > 0)) {
|
||||||
String fee = Wallet.getDisplayAmount(info.fee);
|
String fee = Wallet.getDisplayAmount(info.fee);
|
||||||
@@ -255,16 +270,17 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxFee.setVisibility(View.GONE);
|
tvTxFee.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Context ctx = requireContext();
|
||||||
if (info.isFailed) {
|
if (info.isFailed) {
|
||||||
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
||||||
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.isPending) {
|
} else if (info.isPending) {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor));
|
||||||
} else {
|
} else {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.negativeColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.negativeColor));
|
||||||
}
|
}
|
||||||
Set<String> destinations = new HashSet<>();
|
Set<String> destinations = new HashSet<>();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -299,37 +315,49 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxTransfers.setText(sb.toString());
|
tvTxTransfers.setText(sb.toString());
|
||||||
tvDestination.setText(dstSb.toString());
|
tvDestination.setText(dstSb.toString());
|
||||||
showBtcInfo();
|
showBtcInfo();
|
||||||
|
|
||||||
|
showLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLock() {
|
||||||
|
llWarning.setVisibility(View.GONE);
|
||||||
|
if (info.unlockTime == 0) return;
|
||||||
|
final long blockheight = activityCallback.getDaemonHeight();
|
||||||
|
final long blocks = info.unlockTime - blockheight;
|
||||||
|
final double unlockDays = blocks / (30. * 24);
|
||||||
|
if (unlockDays > 0) {
|
||||||
|
llWarning.setVisibility(View.VISIBLE);
|
||||||
|
tvWarning.setText(getString(R.string.tx_locked, info.unlockTime, blocks, unlockDays));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
void showBtcInfo() {
|
void showBtcInfo() {
|
||||||
if (userNotes.xmrtoKey != null) {
|
if (userNotes.xmrtoKey != null) {
|
||||||
cvXmrTo.setVisibility(View.VISIBLE);
|
cvXmrTo.setVisibility(View.VISIBLE);
|
||||||
String key = userNotes.xmrtoKey;
|
|
||||||
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
|
ShiftService service = ShiftService.findWithTag(userNotes.xmrtoTag);
|
||||||
key = "xmrto-" + key;
|
tvXmrToKeyLabel.setText(getString(R.string.label_send_btc_xmrto_key_lb, service.getLabel()));
|
||||||
}
|
if (service.getIconId() == 0)
|
||||||
tvTxXmrToKey.setText(key);
|
tvXmrToLogo.setVisibility(View.GONE);
|
||||||
|
else
|
||||||
|
tvXmrToLogo.setImageResource(service.getLogoId());
|
||||||
|
|
||||||
|
tvTxXmrToKey.setText(userNotes.xmrtoKey);
|
||||||
|
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||||
switch (userNotes.xmrtoTag) {
|
|
||||||
case "xmrto":
|
ShiftApi shiftApi = service.getShiftApi();
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
if (shiftApi != null) {
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
tvXmrToSupport.setText(getString(R.string.label_send_btc_xmrto_info, service.getLabel()));
|
||||||
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
|
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
break;
|
tvXmrToSupport.setOnClickListener(v -> {
|
||||||
case "side": // defaults in layout - just add underline
|
startActivity(new Intent(Intent.ACTION_VIEW, shiftApi.getQueryOrderUri(userNotes.xmrtoKey)));
|
||||||
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
});
|
||||||
tvXmrToSupport.setOnClickListener(v -> {
|
} else {
|
||||||
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
|
||||||
tvXmrToLogo.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cvXmrTo.setVisibility(View.GONE);
|
cvXmrTo.setVisibility(View.GONE);
|
||||||
@@ -346,7 +374,7 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.tx_info_menu, menu);
|
inflater.inflate(R.menu.tx_info_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
@@ -370,10 +398,11 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
void showSubaddress(View view, final int subaddressIndex);
|
void showSubaddress(View view, final int subaddressIndex);
|
||||||
|
|
||||||
|
long getDaemonHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof TxFragment.Listener) {
|
if (context instanceof TxFragment.Listener) {
|
||||||
this.activityCallback = (TxFragment.Listener) context;
|
this.activityCallback = (TxFragment.Listener) context;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,7 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
|||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.StickyFiatHelper;
|
||||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ import java.util.List;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class WalletFragment extends Fragment
|
public class WalletFragment extends Fragment
|
||||||
implements TransactionInfoAdapter.OnInteractionListener {
|
implements TransactionInfoAdapter.Listener {
|
||||||
private TransactionInfoAdapter adapter;
|
private TransactionInfoAdapter adapter;
|
||||||
private final NumberFormat formatter = NumberFormat.getInstance();
|
private final NumberFormat formatter = NumberFormat.getInstance();
|
||||||
|
|
||||||
@@ -112,15 +113,15 @@ public class WalletFragment extends Fragment
|
|||||||
flExchange = view.findViewById(R.id.flExchange);
|
flExchange = view.findViewById(R.id.flExchange);
|
||||||
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
||||||
setColorFilter(
|
setColorFilter(
|
||||||
ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
|
ThemeHelper.getThemedColor(requireContext(), com.google.android.material.R.attr.colorPrimaryVariant),
|
||||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
|
|
||||||
tvProgress = view.findViewById(R.id.tvProgress);
|
tvProgress = view.findViewById(R.id.tvProgress);
|
||||||
pbProgress = view.findViewById(R.id.pbProgress);
|
pbProgress = view.findViewById(R.id.pbProgress);
|
||||||
tvBalance = view.findViewById(R.id.tvBalance);
|
tvBalance = view.findViewById(R.id.tvBalance);
|
||||||
showBalance(Helper.getDisplayAmount(0));
|
showBalance();
|
||||||
tvUnconfirmedAmount = view.findViewById(R.id.tvUnconfirmedAmount);
|
tvUnconfirmedAmount = view.findViewById(R.id.tvUnconfirmedAmount);
|
||||||
showUnconfirmed(0);
|
showUnconfirmed();
|
||||||
ivSynced = view.findViewById(R.id.ivSynced);
|
ivSynced = view.findViewById(R.id.ivSynced);
|
||||||
|
|
||||||
sCurrency = view.findViewById(R.id.sCurrency);
|
sCurrency = view.findViewById(R.id.sCurrency);
|
||||||
@@ -131,6 +132,7 @@ public class WalletFragment extends Fragment
|
|||||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_balance, currencies);
|
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_balance, currencies);
|
||||||
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
sCurrency.setAdapter(spinnerAdapter);
|
sCurrency.setAdapter(spinnerAdapter);
|
||||||
|
StickyFiatHelper.setPreferredCurrencyPosition(sCurrency);
|
||||||
|
|
||||||
bSend = view.findViewById(R.id.bSend);
|
bSend = view.findViewById(R.id.bSend);
|
||||||
bReceive = view.findViewById(R.id.bReceive);
|
bReceive = view.findViewById(R.id.bReceive);
|
||||||
@@ -182,6 +184,7 @@ public class WalletFragment extends Fragment
|
|||||||
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
|
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
|
||||||
|
StickyFiatHelper.setPreferredFiatSymbol(requireContext(), (String) sCurrency.getSelectedItem());
|
||||||
refreshBalance();
|
refreshBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +208,18 @@ public class WalletFragment extends Fragment
|
|||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showBalance(String balance) {
|
String amountToString(double amount) {
|
||||||
|
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||||
|
double amountB = amount * balanceRate;
|
||||||
|
return Helper.getFormattedAmount(amountB, false);
|
||||||
|
} else { // XMR
|
||||||
|
return Helper.getFormattedAmount(amount, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showBalance() {
|
||||||
|
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
|
String balance = amountToString(amountA);
|
||||||
tvBalance.setText(balance);
|
tvBalance.setText(balance);
|
||||||
final boolean streetMode = activityCallback.isStreetMode();
|
final boolean streetMode = activityCallback.isStreetMode();
|
||||||
if (!streetMode) {
|
if (!streetMode) {
|
||||||
@@ -218,13 +232,14 @@ public class WalletFragment extends Fragment
|
|||||||
setStreetModeBackground(streetMode);
|
setStreetModeBackground(streetMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showUnconfirmed(double unconfirmedAmount) {
|
void showUnconfirmed() {
|
||||||
|
double unconfirmedAmount = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
||||||
if (activityCallback.isStreetMode() || unconfirmedAmount == 0) {
|
if (activityCallback.isStreetMode() || unconfirmedAmount == 0) {
|
||||||
tvUnconfirmedAmount.setText(null);
|
tvUnconfirmedAmount.setText(null);
|
||||||
tvUnconfirmedAmount.setVisibility(View.GONE);
|
tvUnconfirmedAmount.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
String unconfirmed = Helper.getFormattedAmount(unconfirmedAmount, true);
|
String unconfirmed = amountToString(unconfirmedAmount);
|
||||||
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed));
|
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed, balanceCurrency));
|
||||||
tvUnconfirmedAmount.setVisibility(View.VISIBLE);
|
tvUnconfirmedAmount.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,15 +247,8 @@ public class WalletFragment extends Fragment
|
|||||||
void updateBalance() {
|
void updateBalance() {
|
||||||
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
showBalance();
|
||||||
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
showUnconfirmed();
|
||||||
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
|
|
||||||
double amountB = amountA * balanceRate;
|
|
||||||
displayB = Helper.getFormattedAmount(amountB, false);
|
|
||||||
} else { // XMR
|
|
||||||
displayB = Helper.getFormattedAmount(amountA, true);
|
|
||||||
}
|
|
||||||
showBalance(displayB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String balanceCurrency = Helper.BASE_CRYPTO;
|
String balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
@@ -249,11 +257,11 @@ public class WalletFragment extends Fragment
|
|||||||
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
|
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
|
||||||
|
|
||||||
void refreshBalance() {
|
void refreshBalance() {
|
||||||
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
|
||||||
showUnconfirmed(unconfirmedXmr);
|
|
||||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
balanceRate = 1.0;
|
||||||
|
showBalance();
|
||||||
|
showUnconfirmed();
|
||||||
} else { // not XMR
|
} else { // not XMR
|
||||||
String currency = (String) sCurrency.getSelectedItem();
|
String currency = (String) sCurrency.getSelectedItem();
|
||||||
Timber.d(currency);
|
Timber.d(currency);
|
||||||
@@ -298,8 +306,7 @@ public class WalletFragment extends Fragment
|
|||||||
|
|
||||||
public void exchangeFailed() {
|
public void exchangeFailed() {
|
||||||
sCurrency.setSelection(0, true); // default to XMR
|
sCurrency.setSelection(0, true); // default to XMR
|
||||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
showBalance();
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
|
||||||
hideExchanging();
|
hideExchanging();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,13 +318,7 @@ public class WalletFragment extends Fragment
|
|||||||
balanceCurrency = Helper.BASE_CRYPTO;
|
balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
balanceRate = 1.0;
|
balanceRate = 1.0;
|
||||||
} else {
|
} else {
|
||||||
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
StickyFiatHelper.setCurrencyPosition(sCurrency, exchangeRate.getQuoteCurrency());
|
||||||
if (spinnerPosition < 0) { // requested currency not in list
|
|
||||||
Timber.e("Requested currency not in list %s", exchangeRate.getQuoteCurrency());
|
|
||||||
sCurrency.setSelection(0, true);
|
|
||||||
} else {
|
|
||||||
sCurrency.setSelection(spinnerPosition, true);
|
|
||||||
}
|
|
||||||
balanceCurrency = exchangeRate.getQuoteCurrency();
|
balanceCurrency = exchangeRate.getQuoteCurrency();
|
||||||
balanceRate = exchangeRate.getRate();
|
balanceRate = exchangeRate.getRate();
|
||||||
}
|
}
|
||||||
@@ -443,12 +444,13 @@ public class WalletFragment extends Fragment
|
|||||||
String sync;
|
String sync;
|
||||||
if (!activityCallback.hasBoundService())
|
if (!activityCallback.hasBoundService())
|
||||||
throw new IllegalStateException("WalletService not bound.");
|
throw new IllegalStateException("WalletService not bound.");
|
||||||
|
ivSynced.setVisibility(View.GONE);
|
||||||
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
||||||
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||||
if (!wallet.isSynchronized()) {
|
if (!wallet.isSynchronized()) {
|
||||||
long daemonHeight = activityCallback.getDaemonHeight();
|
final long daemonHeight = getDaemonHeight();
|
||||||
long walletHeight = wallet.getBlockChainHeight();
|
final long walletHeight = wallet.getBlockChainHeight();
|
||||||
long n = daemonHeight - walletHeight;
|
final long n = daemonHeight - walletHeight;
|
||||||
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
|
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
|
||||||
if (firstBlock == 0) {
|
if (firstBlock == 0) {
|
||||||
firstBlock = walletHeight;
|
firstBlock = walletHeight;
|
||||||
@@ -456,7 +458,6 @@ public class WalletFragment extends Fragment
|
|||||||
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
|
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
|
||||||
if (x == 0) x = 101; // indeterminate
|
if (x == 0) x = 101; // indeterminate
|
||||||
setProgress(x);
|
setProgress(x);
|
||||||
ivSynced.setVisibility(View.GONE);
|
|
||||||
} else {
|
} else {
|
||||||
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
||||||
ivSynced.setVisibility(View.VISIBLE);
|
ivSynced.setVisibility(View.VISIBLE);
|
||||||
@@ -556,4 +557,9 @@ public class WalletFragment extends Fragment
|
|||||||
} else
|
} else
|
||||||
ivStreetGunther.setImageDrawable(null);
|
ivStreetGunther.setImageDrawable(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDaemonHeight() {
|
||||||
|
return activityCallback.getDaemonHeight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,6 @@ import android.content.res.Configuration;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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;
|
||||||
@@ -36,7 +35,6 @@ 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());
|
||||||
}
|
}
|
||||||
|
@@ -24,43 +24,38 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
public class BarcodeData {
|
public class BarcodeData {
|
||||||
|
|
||||||
public enum Security {
|
public enum Security {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
OA_NO_DNSSEC,
|
OA_NO_DNSSEC,
|
||||||
OA_DNSSEC
|
OA_DNSSEC
|
||||||
}
|
}
|
||||||
|
|
||||||
final public Crypto asset;
|
final private Set<Crypto> possibleAssets = new HashSet<>();
|
||||||
final public List<Crypto> ambiguousAssets;
|
private String address = null;
|
||||||
final public String address;
|
private String addressName = null;
|
||||||
final public String addressName;
|
private String amount = null;
|
||||||
final public String amount;
|
private String description = null;
|
||||||
final public String description;
|
private Security security = null;
|
||||||
final public Security security;
|
|
||||||
|
|
||||||
public BarcodeData(List<Crypto> assets, String address) {
|
public BarcodeData(List<Crypto> assets, String address) {
|
||||||
if (assets.isEmpty())
|
if (assets.isEmpty())
|
||||||
throw new IllegalArgumentException("no assets specified");
|
throw new IllegalArgumentException("no assets specified");
|
||||||
this.addressName = null;
|
security = Security.NORMAL;
|
||||||
this.description = null;
|
|
||||||
this.amount = null;
|
|
||||||
this.security = Security.NORMAL;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
if (assets.size() == 1) {
|
possibleAssets.addAll(assets);
|
||||||
this.asset = assets.get(0);
|
|
||||||
this.ambiguousAssets = null;
|
|
||||||
} else {
|
|
||||||
this.asset = null;
|
|
||||||
this.ambiguousAssets = assets;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
||||||
@@ -68,8 +63,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
||||||
this.ambiguousAssets = null;
|
possibleAssets.add(asset);
|
||||||
this.asset = asset;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.addressName = addressName;
|
this.addressName = addressName;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@@ -82,7 +76,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getUriString() {
|
public String getUriString() {
|
||||||
if (asset != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
if (getAsset() != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(Crypto.XMR.getUriScheme())
|
sb.append(Crypto.XMR.getUriScheme())
|
||||||
.append(':')
|
.append(':')
|
||||||
@@ -227,6 +221,20 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAmbiguous() {
|
public boolean isAmbiguous() {
|
||||||
return ambiguousAssets != null;
|
return possibleAssets.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crypto getAsset() {
|
||||||
|
if (possibleAssets.size() == 1) {
|
||||||
|
return possibleAssets.iterator().next();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if we still have possible assets
|
||||||
|
public boolean filter(Set<Crypto> assets) {
|
||||||
|
possibleAssets.retainAll(assets);
|
||||||
|
return !possibleAssets.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user