mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-07 11:35:59 +02:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
@@ -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,15 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
|
||||||
buildToolsVersion '33.0.2'
|
|
||||||
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 3307
|
versionCode 4006
|
||||||
versionName "3.3.7 'Pocket Change'"
|
versionName "4.0.6 'Sidekick'"
|
||||||
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'
|
||||||
@@ -112,6 +112,17 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
namespace 'com.m2049r.xmrwallet'
|
namespace 'com.m2049r.xmrwallet'
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
all {
|
||||||
|
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static def getId(name) {
|
static def getId(name) {
|
||||||
@@ -121,41 +132,40 @@ static def getId(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))
|
||||||
|
|
||||||
implementation 'androidx.core:core:1.10.1'
|
implementation 'androidx.core:core:1.13.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.preference:preference:1.2.0'
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
|
|
||||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
implementation "com.squareup.okhttp3:okhttp:4.9.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.0"
|
||||||
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.8.2'
|
||||||
|
|
||||||
implementation 'dnsjava:dnsjava:2.1.9'
|
implementation 'dnsjava:dnsjava:3.5.3'
|
||||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
implementation 'org.slf4j:slf4j-nop:2.0.11'
|
||||||
implementation 'org.slf4j:slf4j-nop:1.7.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:20231013'
|
||||||
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.30'
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,15 @@
|
|||||||
<?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" />
|
||||||
@@ -8,6 +17,7 @@
|
|||||||
<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.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@@ -94,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"
|
||||||
|
@@ -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.
|
||||||
@@ -18,8 +18,6 @@
|
|||||||
#include "monerujo.h"
|
#include "monerujo.h"
|
||||||
#include "wallet2_api.h"
|
#include "wallet2_api.h"
|
||||||
|
|
||||||
//TODO explicit casting jlong, jint, jboolean to avoid warnings
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@@ -34,7 +32,6 @@ extern "C"
|
|||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__)
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__)
|
||||||
|
|
||||||
static JavaVM *cachedJVM;
|
static JavaVM *cachedJVM;
|
||||||
static jclass class_String;
|
|
||||||
static jclass class_ArrayList;
|
static jclass class_ArrayList;
|
||||||
static jclass class_WalletListener;
|
static jclass class_WalletListener;
|
||||||
static jclass class_CoinsInfo;
|
static jclass class_CoinsInfo;
|
||||||
@@ -42,17 +39,11 @@ static jclass class_TransactionInfo;
|
|||||||
static jclass class_Transfer;
|
static jclass class_Transfer;
|
||||||
static jclass class_Ledger;
|
static jclass class_Ledger;
|
||||||
static jclass class_WalletStatus;
|
static jclass class_WalletStatus;
|
||||||
|
static jclass class_BluetoothService;
|
||||||
|
static jclass class_SidekickService;
|
||||||
|
|
||||||
std::mutex _listenerMutex;
|
std::mutex _listenerMutex;
|
||||||
|
|
||||||
//void jstringToString(JNIEnv *env, std::string &str, jstring jstr) {
|
|
||||||
// if (!jstr) return;
|
|
||||||
// const int len = env->GetStringUTFLength(jstr);
|
|
||||||
// const char *chars = env->GetStringUTFChars(jstr, nullptr);
|
|
||||||
// str.assign(chars, len);
|
|
||||||
// env->ReleaseStringUTFChars(jstr, chars);
|
|
||||||
//}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||||
cachedJVM = jvm;
|
cachedJVM = jvm;
|
||||||
LOGI("JNI_OnLoad");
|
LOGI("JNI_OnLoad");
|
||||||
@@ -62,8 +53,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
}
|
}
|
||||||
//LOGI("JNI_OnLoad ok");
|
//LOGI("JNI_OnLoad ok");
|
||||||
|
|
||||||
class_String = static_cast<jclass>(jenv->NewGlobalRef(
|
|
||||||
jenv->FindClass("java/lang/String")));
|
|
||||||
class_ArrayList = static_cast<jclass>(jenv->NewGlobalRef(
|
class_ArrayList = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
jenv->FindClass("java/util/ArrayList")));
|
jenv->FindClass("java/util/ArrayList")));
|
||||||
class_CoinsInfo = static_cast<jclass>(jenv->NewGlobalRef(
|
class_CoinsInfo = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
@@ -78,6 +67,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
|
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
|
||||||
class_WalletStatus = static_cast<jclass>(jenv->NewGlobalRef(
|
class_WalletStatus = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status")));
|
jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status")));
|
||||||
|
class_BluetoothService = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
|
jenv->FindClass("com/m2049r/xmrwallet/service/BluetoothService")));
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -1686,6 +1677,79 @@ int LedgerFind(char *buffer, size_t len) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SidekickWallet Stuff
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BtExchange - exchange data with Monerujo Device
|
||||||
|
* @param request - buffer for data to send
|
||||||
|
* @param request_len - length of data to send
|
||||||
|
* @param response - buffer for received data
|
||||||
|
* @param max_resp_len - size of receive buffer
|
||||||
|
*
|
||||||
|
* @return length of received data in response or -1 if error, -2 if response buffer too small
|
||||||
|
*/
|
||||||
|
int BtExchange(
|
||||||
|
unsigned char *request,
|
||||||
|
unsigned int request_len,
|
||||||
|
unsigned char *response,
|
||||||
|
unsigned int max_resp_len) {
|
||||||
|
JNIEnv *jenv;
|
||||||
|
int envStat = attachJVM(&jenv);
|
||||||
|
if (envStat == JNI_ERR) return -16;
|
||||||
|
|
||||||
|
jmethodID exchangeMethod = jenv->GetStaticMethodID(class_BluetoothService, "Exchange",
|
||||||
|
"([B)[B");
|
||||||
|
|
||||||
|
auto reqLen = static_cast<jsize>(request_len);
|
||||||
|
jbyteArray reqData = jenv->NewByteArray(reqLen);
|
||||||
|
jenv->SetByteArrayRegion(reqData, 0, reqLen, (jbyte *) request);
|
||||||
|
LOGD("BtExchange cmd: 0x%02x with %u bytes", request[0], reqLen);
|
||||||
|
auto dataRecv = (jbyteArray)
|
||||||
|
jenv->CallStaticObjectMethod(class_BluetoothService, exchangeMethod, reqData);
|
||||||
|
jenv->DeleteLocalRef(reqData);
|
||||||
|
if (dataRecv == nullptr) {
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
LOGD("BtExchange: error reading");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
jsize respLen = jenv->GetArrayLength(dataRecv);
|
||||||
|
LOGD("BtExchange response is %u bytes", respLen);
|
||||||
|
if (respLen <= max_resp_len) {
|
||||||
|
jenv->GetByteArrayRegion(dataRecv, 0, respLen, (jbyte *) response);
|
||||||
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
return static_cast<int>(respLen);;
|
||||||
|
} else {
|
||||||
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
LOGE("BtExchange response buffer too small: %u < %u", respLen, max_resp_len);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ConfirmTransfers
|
||||||
|
* @param transfers - string of "fee (':' address ':' amount)+"
|
||||||
|
*
|
||||||
|
* @return true on accept, false on reject
|
||||||
|
*/
|
||||||
|
bool ConfirmTransfers(const char *transfers) {
|
||||||
|
JNIEnv *jenv;
|
||||||
|
int envStat = attachJVM(&jenv);
|
||||||
|
if (envStat == JNI_ERR) return -16;
|
||||||
|
|
||||||
|
jmethodID confirmMethod = jenv->GetStaticMethodID(class_SidekickService, "ConfirmTransfers",
|
||||||
|
"(Ljava/lang/String;)Z");
|
||||||
|
|
||||||
|
jstring _transfers = jenv->NewStringUTF(transfers);
|
||||||
|
auto confirmed =
|
||||||
|
jenv->CallStaticBooleanMethod(class_SidekickService, confirmMethod, _transfers);
|
||||||
|
jenv->DeleteLocalRef(_transfers);
|
||||||
|
return confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -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 |
@@ -16,12 +16,26 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||||
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
||||||
@@ -35,18 +49,13 @@ public class BaseActivity extends SecureActivity
|
|||||||
|
|
||||||
ProgressDialog progressDialog = null;
|
ProgressDialog progressDialog = null;
|
||||||
|
|
||||||
private class SimpleProgressDialog extends ProgressDialog {
|
private static class SimpleProgressDialog extends ProgressDialog {
|
||||||
|
|
||||||
SimpleProgressDialog(Context context, int msgId) {
|
SimpleProgressDialog(Context context, int msgId) {
|
||||||
super(context);
|
super(context);
|
||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
setMessage(context.getString(msgId));
|
setMessage(context.getString(msgId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
// prevent back button
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,13 +68,15 @@ public class BaseActivity extends SecureActivity
|
|||||||
progressDialog = new SimpleProgressDialog(BaseActivity.this, msgId);
|
progressDialog = new SimpleProgressDialog(BaseActivity.this, msgId);
|
||||||
if (delayMillis > 0) {
|
if (delayMillis > 0) {
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.postDelayed(new Runnable() {
|
handler.postDelayed(() -> {
|
||||||
public void run() {
|
if (progressDialog != null) {
|
||||||
if (progressDialog != null) progressDialog.show();
|
progressDialog.show();
|
||||||
|
disableBackPressed();
|
||||||
}
|
}
|
||||||
}, delayMillis);
|
}, delayMillis);
|
||||||
} else {
|
} else {
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
|
disableBackPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +86,7 @@ public class BaseActivity extends SecureActivity
|
|||||||
progressDialog = new LedgerProgressDialog(BaseActivity.this, mode);
|
progressDialog = new LedgerProgressDialog(BaseActivity.this, mode);
|
||||||
Ledger.setListener((Ledger.Listener) progressDialog);
|
Ledger.setListener((Ledger.Listener) progressDialog);
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
|
disableBackPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -87,6 +99,28 @@ public class BaseActivity extends SecureActivity
|
|||||||
progressDialog.dismiss();
|
progressDialog.dismiss();
|
||||||
}
|
}
|
||||||
progressDialog = null;
|
progressDialog = null;
|
||||||
|
enableBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
// no going back
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void disableBackPressed() {
|
||||||
|
backPressedCallback.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableBackPressed() {
|
||||||
|
backPressedCallback.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final int RELEASE_WAKE_LOCK_DELAY = 5000; // millisconds
|
static final int RELEASE_WAKE_LOCK_DELAY = 5000; // millisconds
|
||||||
@@ -136,4 +170,87 @@ public class BaseActivity extends SecureActivity
|
|||||||
barcodeData = null;
|
barcodeData = null;
|
||||||
return popped;
|
return popped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNetworkAvailable() {
|
||||||
|
ConnectivityManager connectivityManager = (ConnectivityManager) getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
Network network = connectivityManager.getActiveNetwork();
|
||||||
|
if (network == null) return false;
|
||||||
|
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(network);
|
||||||
|
return networkCapabilities != null && (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH));
|
||||||
|
} else {
|
||||||
|
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||||
|
return networkInfo != null && networkInfo.isConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private final int REQUEST_CODE_BLUETOOTH_PERMISSIONS = 32423;
|
||||||
|
|
||||||
|
void btPermissionGranted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
if (requestCode == REQUEST_CODE_BLUETOOTH_PERMISSIONS) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
btPermissionGranted();
|
||||||
|
} // else onResume() takes care of trying again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBtPermissionsDialog() {
|
||||||
|
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
|
||||||
|
alertDialogBuilder.setMessage(R.string.bluetooth_permissions);
|
||||||
|
alertDialogBuilder.setPositiveButton(R.string.bluetooth_permissions_ok,
|
||||||
|
(dialog, which) -> requestBtPermissions());
|
||||||
|
alertDialogBuilder.setCancelable(false);
|
||||||
|
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAppInfoDialog() {
|
||||||
|
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
|
||||||
|
alertDialogBuilder.setMessage(R.string.bluetooth_permissions);
|
||||||
|
alertDialogBuilder.setPositiveButton(R.string.bluetooth_permissions_settings, (dialog, which) -> {
|
||||||
|
Intent i = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||||
|
i.setData(Uri.parse("package:" + getPackageName()));
|
||||||
|
startActivity(i);
|
||||||
|
});
|
||||||
|
alertDialogBuilder.setNegativeButton(R.string.bluetooth_permissions_cancel, (dialog, which) -> {
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
alertDialogBuilder.setCancelable(false);
|
||||||
|
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestBtPermissions() {
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN}, REQUEST_CODE_BLUETOOTH_PERMISSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean firstCheck = true;
|
||||||
|
|
||||||
|
void checkBtPermissions() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
if ((ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) ||
|
||||||
|
(ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)) {
|
||||||
|
if (shouldShowRequestPermissionRationale(android.Manifest.permission.BLUETOOTH_SCAN) || shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) {
|
||||||
|
showBtPermissionsDialog();
|
||||||
|
} else {
|
||||||
|
if (firstCheck) {
|
||||||
|
requestBtPermissions();
|
||||||
|
} else {
|
||||||
|
showAppInfoDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btPermissionGranted();
|
||||||
|
}
|
||||||
|
firstCheck = false;
|
||||||
|
} else {
|
||||||
|
btPermissionGranted();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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
@@ -18,7 +18,6 @@ package com.m2049r.xmrwallet;
|
|||||||
|
|
||||||
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;
|
||||||
@@ -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) == '.') {
|
||||||
@@ -450,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:
|
||||||
@@ -498,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:
|
||||||
@@ -515,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);
|
||||||
|
|
||||||
@@ -555,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;
|
||||||
@@ -581,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 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) {
|
||||||
@@ -160,6 +168,9 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
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 +254,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 +286,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 +432,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
|
||||||
|
37
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
37
app/src/main/java/com/m2049r/xmrwallet/LockFragment.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class LockFragment extends Fragment {
|
||||||
|
@Override
|
||||||
|
public View onCreateView(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
@@ -35,6 +35,7 @@ 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;
|
||||||
@@ -45,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;
|
||||||
@@ -60,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,
|
||||||
@@ -114,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);
|
||||||
}
|
}
|
||||||
@@ -145,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 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) {
|
||||||
@@ -164,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);
|
||||||
@@ -171,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);
|
||||||
@@ -186,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);
|
||||||
@@ -194,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());
|
||||||
@@ -287,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
|
||||||
@@ -295,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);
|
||||||
@@ -326,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);
|
||||||
@@ -360,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,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();
|
||||||
@@ -390,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();
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ 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.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;
|
||||||
@@ -145,6 +146,13 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private 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 +195,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,6 +206,7 @@ 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
|
||||||
@@ -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));
|
||||||
@@ -575,6 +586,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreDefaultNodes() {
|
void restoreDefaultNodes() {
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2017 m2049r
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
|
||||||
|
|
||||||
public interface OnBackPressedListener {
|
|
||||||
boolean onBackPressed();
|
|
||||||
}
|
|
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* 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.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
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 (bluetoothAdapter.isDiscovering()) {
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
// the the activity we are connected? why? it can ask the bluetoothservice...
|
||||||
|
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<>();
|
||||||
|
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) {
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInteraction(final View view, final BluetoothInfo item) {
|
||||||
|
Timber.d("onInteraction %s", item);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,8 @@ 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.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
@@ -36,7 +36,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());
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BluetoothInfo {
|
||||||
|
@Getter
|
||||||
|
final private String name;
|
||||||
|
@Getter
|
||||||
|
final private String address;
|
||||||
|
@Getter
|
||||||
|
private boolean bonded;
|
||||||
|
|
||||||
|
public BluetoothInfo(BluetoothDevice device) {
|
||||||
|
name = device.getName().trim();
|
||||||
|
address = device.getAddress();
|
||||||
|
bonded = device.getBondState() == BluetoothDevice.BOND_BONDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Comparator<BluetoothInfo> NameComparator = (o1, o2) -> o1.name.compareTo(o2.name);
|
||||||
|
}
|
@@ -29,10 +29,12 @@ public enum DefaultNodes {
|
|||||||
HASHVAULT("nodes.hashvault.pro:18081"),
|
HASHVAULT("nodes.hashvault.pro:18081"),
|
||||||
MONEROWORLD("node.moneroworld.com:18089"),
|
MONEROWORLD("node.moneroworld.com:18089"),
|
||||||
XMRTW("opennode.xmr-tw.org:18089"),
|
XMRTW("opennode.xmr-tw.org:18089"),
|
||||||
|
ds_jetzt("monero.ds-jetzt.de:18089"),
|
||||||
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
||||||
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
|
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
|
||||||
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
|
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
|
||||||
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion");
|
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion"),
|
||||||
|
ds_jetzt_onion("qvlr4w7yhnjrdg3txa72jwtpnjn4ezsrivzvocbnvpfbdo342fahhoad.onion:18089/mainnet/ds-jetzt.onion");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final String uri;
|
private final String uri;
|
||||||
|
@@ -26,6 +26,7 @@ import com.m2049r.xmrwallet.model.Wallet;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -136,8 +137,9 @@ public class TxData implements Parcelable {
|
|||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
final static public int POCKETCHANGE_IDX = 1; // subaddress index of first pocketchange slot
|
final static public int POCKETCHANGE_IDX = 1; // subaddress index of first pocketchange slot
|
||||||
final static public int POCKETCHANGE_SLOTS = 10; // number of pocketchange slots
|
final static public int POCKETCHANGE_SLOTS_MIN = 6; // min number of pocketchange slots
|
||||||
final static public int POCKETCHANGE_IDX_MAX = POCKETCHANGE_IDX + POCKETCHANGE_SLOTS - 1;
|
final static public int POCKETCHANGE_SLOTS_MAX = 14; // max number of pocketchange slots
|
||||||
|
final static public int POCKETCHANGE_IDX_MAX = POCKETCHANGE_IDX + POCKETCHANGE_SLOTS_MAX - 1;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
static private class PocketChangeSlot {
|
static private class PocketChangeSlot {
|
||||||
@@ -171,8 +173,8 @@ public class TxData implements Parcelable {
|
|||||||
List<CoinsInfo> coins = wallet.getCoinsInfos(true);
|
List<CoinsInfo> coins = wallet.getCoinsInfos(true);
|
||||||
Set<Integer> spendableSubaddressIdx = new HashSet<>();
|
Set<Integer> spendableSubaddressIdx = new HashSet<>();
|
||||||
PocketChangeSlot reserves = new PocketChangeSlot(); // everything not in a slot spendable
|
PocketChangeSlot reserves = new PocketChangeSlot(); // everything not in a slot spendable
|
||||||
PocketChangeSlot[] slots = new PocketChangeSlot[POCKETCHANGE_SLOTS];
|
PocketChangeSlot[] slots = new PocketChangeSlot[POCKETCHANGE_SLOTS_MAX];
|
||||||
for (int i = 0; i < POCKETCHANGE_SLOTS; i++) {
|
for (int i = 0; i < POCKETCHANGE_SLOTS_MAX; i++) {
|
||||||
slots[i] = new PocketChangeSlot();
|
slots[i] = new PocketChangeSlot();
|
||||||
}
|
}
|
||||||
for (CoinsInfo coin : coins) {
|
for (CoinsInfo coin : coins) {
|
||||||
@@ -205,7 +207,8 @@ public class TxData implements Parcelable {
|
|||||||
// find any slots to fill if possible:
|
// find any slots to fill if possible:
|
||||||
List<Integer> slotsToFill = new ArrayList<>();
|
List<Integer> slotsToFill = new ArrayList<>();
|
||||||
List<Long> slotToFillAmounts = new ArrayList<>();
|
List<Long> slotToFillAmounts = new ArrayList<>();
|
||||||
for (int i = 0; i < POCKETCHANGE_SLOTS; i++) {
|
final int randomSlotCount = new Random().nextInt(POCKETCHANGE_SLOTS_MAX - POCKETCHANGE_SLOTS_MIN + 1) + POCKETCHANGE_SLOTS_MIN;
|
||||||
|
for (int i = 0; i < randomSlotCount; i++) {
|
||||||
if (slots[i].getAmount() < pocketChangeAmount) {
|
if (slots[i].getAmount() < pocketChangeAmount) {
|
||||||
final long topupAmount = pocketChangeAmount - slots[i].getAmount();
|
final long topupAmount = pocketChangeAmount - slots[i].getAmount();
|
||||||
if (topupAmount <= spendableAmount) {
|
if (topupAmount <= spendableAmount) {
|
||||||
|
@@ -74,6 +74,9 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
private View llConfirmSend;
|
private View llConfirmSend;
|
||||||
private Button bSend;
|
private Button bSend;
|
||||||
private View pbProgressSend;
|
private View pbProgressSend;
|
||||||
|
private TextView tvTxChange;
|
||||||
|
private View llPocketChange;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
@@ -92,6 +95,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
|
|
||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
tvTxTotal = view.findViewById(R.id.tvTxTotal);
|
tvTxTotal = view.findViewById(R.id.tvTxTotal);
|
||||||
|
tvTxChange = view.findViewById(R.id.tvTxChange);
|
||||||
|
llPocketChange = view.findViewById(R.id.llPocketChange);
|
||||||
|
|
||||||
llStageA = view.findViewById(R.id.llStageA);
|
llStageA = view.findViewById(R.id.llStageA);
|
||||||
evStageA = view.findViewById(R.id.evStageA);
|
evStageA = view.findViewById(R.id.evStageA);
|
||||||
@@ -217,6 +222,13 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
|
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
|
||||||
tvTxTotal.setText(Wallet.getDisplayAmount(
|
tvTxTotal.setText(Wallet.getDisplayAmount(
|
||||||
pendingTransaction.getFee() + pendingTransaction.getAmount()));
|
pendingTransaction.getFee() + pendingTransaction.getAmount()));
|
||||||
|
final long change = pendingTransaction.getPocketChange();
|
||||||
|
if (change > 0) {
|
||||||
|
llPocketChange.setVisibility(View.VISIBLE);
|
||||||
|
tvTxChange.setText(Wallet.getDisplayAmount(change));
|
||||||
|
} else {
|
||||||
|
llPocketChange.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
updateSendButton();
|
updateSendButton();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -37,7 +38,6 @@ import androidx.fragment.app.FragmentStatePagerAdapter;
|
|||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import com.google.android.material.transition.MaterialContainerTransform;
|
import com.google.android.material.transition.MaterialContainerTransform;
|
||||||
import com.m2049r.xmrwallet.OnBackPressedListener;
|
|
||||||
import com.m2049r.xmrwallet.OnUriScannedListener;
|
import com.m2049r.xmrwallet.OnUriScannedListener;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.WalletActivity;
|
import com.m2049r.xmrwallet.WalletActivity;
|
||||||
@@ -63,7 +63,7 @@ public class SendFragment extends Fragment
|
|||||||
SendAmountWizardFragment.Listener,
|
SendAmountWizardFragment.Listener,
|
||||||
SendConfirmWizardFragment.Listener,
|
SendConfirmWizardFragment.Listener,
|
||||||
SendSuccessWizardFragment.Listener,
|
SendSuccessWizardFragment.Listener,
|
||||||
OnBackPressedListener, OnUriScannedListener {
|
OnUriScannedListener {
|
||||||
|
|
||||||
final static public int MIXIN = 0;
|
final static public int MIXIN = 0;
|
||||||
|
|
||||||
@@ -248,16 +248,18 @@ public class SendFragment extends Fragment
|
|||||||
private SpendViewPager spendViewPager;
|
private SpendViewPager spendViewPager;
|
||||||
private SpendPagerAdapter pagerAdapter;
|
private SpendPagerAdapter pagerAdapter;
|
||||||
|
|
||||||
@Override
|
OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
|
||||||
public boolean onBackPressed() {
|
@Override
|
||||||
if (isComitted()) return true; // no going back
|
public void handleOnBackPressed() {
|
||||||
if (spendViewPager.getCurrentItem() == 0) {
|
if (isComitted()) return; // no going back
|
||||||
return false;
|
if (spendViewPager.getCurrentItem() == 0) {
|
||||||
} else {
|
setEnabled(false);
|
||||||
spendViewPager.previous();
|
requireActivity().getOnBackPressedDispatcher().onBackPressed();
|
||||||
return true;
|
} else {
|
||||||
|
spendViewPager.previous();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onUriScanned(BarcodeData barcodeData) {
|
public boolean onUriScanned(BarcodeData barcodeData) {
|
||||||
@@ -546,8 +548,9 @@ public class SendFragment extends Fragment
|
|||||||
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
||||||
transform.setDrawingViewId(R.id.fragment_container);
|
transform.setDrawingViewId(R.id.fragment_container);
|
||||||
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||||
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
|
transform.setAllContainerColors(ThemeHelper.getThemedColor(requireContext(), android.R.attr.colorBackground));
|
||||||
setSharedElementEnterTransition(transform);
|
setSharedElementEnterTransition(transform);
|
||||||
|
requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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.layout;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.data.BluetoothInfo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BluetoothInfoAdapter extends RecyclerView.Adapter<BluetoothInfoAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
public interface OnInteractionListener {
|
||||||
|
void onInteraction(View view, BluetoothInfo item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<BluetoothInfo> items = new ArrayList<>();
|
||||||
|
private final OnInteractionListener listener;
|
||||||
|
|
||||||
|
public BluetoothInfoAdapter(OnInteractionListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BluetoothInfoDiff extends DiffCallback<BluetoothInfo> {
|
||||||
|
|
||||||
|
public BluetoothInfoDiff(List<BluetoothInfo> oldList, List<BluetoothInfo> newList) {
|
||||||
|
super(oldList, newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
return mOldList.get(oldItemPosition).getAddress().equals(mNewList.get(newItemPosition).getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
final BluetoothInfo oldItem = mOldList.get(oldItemPosition);
|
||||||
|
final BluetoothInfo newItem = mNewList.get(newItemPosition);
|
||||||
|
return oldItem.equals(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull
|
||||||
|
ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bluetooth, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(final @NonNull ViewHolder holder, int position) {
|
||||||
|
holder.bind(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(BluetoothInfo item) {
|
||||||
|
if (item == null) return;
|
||||||
|
List<BluetoothInfo> newItems = new ArrayList<>(items);
|
||||||
|
if (!items.contains(item))
|
||||||
|
newItems.add(item);
|
||||||
|
setItems(newItems); // in case the nodeinfo has changed
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(Collection<BluetoothInfo> newItemsCollection) {
|
||||||
|
List<BluetoothInfo> newItems;
|
||||||
|
if (newItemsCollection != null) {
|
||||||
|
newItems = new ArrayList<>(newItemsCollection);
|
||||||
|
Collections.sort(newItems, BluetoothInfo.NameComparator);
|
||||||
|
} else {
|
||||||
|
newItems = new ArrayList<>();
|
||||||
|
}
|
||||||
|
final BluetoothInfoAdapter.BluetoothInfoDiff diffCallback = new BluetoothInfoAdapter.BluetoothInfoDiff(items, newItems);
|
||||||
|
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
|
||||||
|
items.clear();
|
||||||
|
items.addAll(newItems);
|
||||||
|
diffResult.dispatchUpdatesTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean itemsClickable = true;
|
||||||
|
|
||||||
|
public void allowClick(boolean clickable) {
|
||||||
|
itemsClickable = clickable;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||||
|
final TextView tvName;
|
||||||
|
final TextView tvAddress;
|
||||||
|
BluetoothInfo item;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
tvName = itemView.findViewById(R.id.tvName);
|
||||||
|
tvAddress = itemView.findViewById(R.id.tvAddress);
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(int position) {
|
||||||
|
item = items.get(position);
|
||||||
|
tvName.setText(item.getName());
|
||||||
|
tvAddress.setText(item.getAddress());
|
||||||
|
itemView.setClickable(itemsClickable);
|
||||||
|
itemView.setEnabled(itemsClickable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (listener != null) {
|
||||||
|
int position = getBindingAdapterPosition(); // gets item position
|
||||||
|
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
|
||||||
|
final BluetoothInfo node = items.get(position);
|
||||||
|
allowClick(false);
|
||||||
|
listener.onInteraction(view, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -86,7 +86,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean needsTransactionUpdateOnNewBlock() {
|
public boolean needsTransactionUpdateOnNewBlock() {
|
||||||
return (infoItems.size() > 0) && !infoItems.get(0).isConfirmed();
|
return (!infoItems.isEmpty()) && !infoItems.get(0).isConfirmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> {
|
private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> {
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
package com.m2049r.xmrwallet.ledger;
|
||||||
|
|
||||||
|
public interface Hardware {
|
||||||
|
}
|
@@ -35,12 +35,11 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Ledger {
|
public class Ledger implements Hardware {
|
||||||
static final public boolean ENABLED = true;
|
static final public boolean ENABLED = true;
|
||||||
// 5:20 is same as wallet2.cpp::restore()
|
// 5:20 is same as wallet2.cpp::restore()
|
||||||
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
||||||
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
||||||
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
|
|
||||||
|
|
||||||
private static final byte PROTOCOL_VERSION = 0x03;
|
private static final byte PROTOCOL_VERSION = 0x03;
|
||||||
public static final int SW_OK = 0x9000;
|
public static final int SW_OK = 0x9000;
|
||||||
|
@@ -46,11 +46,6 @@ public class LedgerProgressDialog extends ProgressDialog implements Ledger.Liste
|
|||||||
setMessage(context.getString(R.string.progress_ledger_progress));
|
setMessage(context.getString(R.string.progress_ledger_progress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
// prevent back button
|
|
||||||
}
|
|
||||||
|
|
||||||
private int firstSubaddress = Integer.MAX_VALUE;
|
private int firstSubaddress = Integer.MAX_VALUE;
|
||||||
|
|
||||||
private boolean validate = false;
|
private boolean validate = false;
|
||||||
|
@@ -111,7 +111,11 @@ public class Wallet {
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public enum Device {
|
public enum Device {
|
||||||
Device_Undefined(0, 0), Device_Software(50, 200), Device_Ledger(5, 20);
|
Undefined(0, 0),
|
||||||
|
Software(50, 200),
|
||||||
|
Ledger(5, 20),
|
||||||
|
Trezor(5, 20),
|
||||||
|
Sidekick(5, 20);
|
||||||
private final int accountLookahead;
|
private final int accountLookahead;
|
||||||
private final int subaddressLookahead;
|
private final int subaddressLookahead;
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ package com.m2049r.xmrwallet.model;
|
|||||||
|
|
||||||
import com.m2049r.xmrwallet.XmrWalletApplication;
|
import com.m2049r.xmrwallet.XmrWalletApplication;
|
||||||
import com.m2049r.xmrwallet.data.Node;
|
import com.m2049r.xmrwallet.data.Node;
|
||||||
import com.m2049r.xmrwallet.ledger.Ledger;
|
|
||||||
import com.m2049r.xmrwallet.util.RestoreHeight;
|
import com.m2049r.xmrwallet.util.RestoreHeight;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -161,10 +160,12 @@ public class WalletManager {
|
|||||||
String spendKeyString);
|
String spendKeyString);
|
||||||
|
|
||||||
public Wallet createWalletFromDevice(File aFile, String password, long restoreHeight,
|
public Wallet createWalletFromDevice(File aFile, String password, long restoreHeight,
|
||||||
String deviceName) {
|
Wallet.Device device) {
|
||||||
|
final String lookahead = device.getAccountLookahead() + ":" + device.getSubaddressLookahead();
|
||||||
|
Timber.d("Creating from %s with %s lookahead", device, lookahead);
|
||||||
long walletHandle = createWalletFromDeviceJ(aFile.getAbsolutePath(), password,
|
long walletHandle = createWalletFromDeviceJ(aFile.getAbsolutePath(), password,
|
||||||
getNetworkType().getValue(), deviceName, restoreHeight,
|
getNetworkType().getValue(), device.name(), restoreHeight,
|
||||||
Ledger.SUBADDRESS_LOOKAHEAD);
|
lookahead);
|
||||||
Wallet wallet = new Wallet(walletHandle);
|
Wallet wallet = new Wallet(walletHandle);
|
||||||
manageWallet(wallet);
|
manageWallet(wallet);
|
||||||
return wallet;
|
return wallet;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user