mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a4b9a7c6fb | ||
![]() |
9f01155cb7 | ||
![]() |
08e8a48138 | ||
![]() |
551c3b9fb6 | ||
![]() |
2258cb7096 | ||
![]() |
6d61841cf3 | ||
![]() |
c65508d288 | ||
![]() |
2c3f582672 | ||
![]() |
f46ba75771 | ||
![]() |
0ce5f2b6ca | ||
![]() |
110057c294 | ||
![]() |
7553d3c5f4 | ||
![]() |
317976b34a | ||
![]() |
6ad423567f | ||
![]() |
d497158856 | ||
![]() |
f4cada5fa1 | ||
![]() |
352f0ad09c | ||
![]() |
ff1a9c1570 | ||
![]() |
fa811a39a2 | ||
![]() |
cf5018be33 | ||
![]() |
8ec027f9d4 | ||
![]() |
f28428e677 | ||
![]() |
294084bec5 | ||
![]() |
4349907627 | ||
![]() |
f7cef24a83 | ||
![]() |
2774f99b15 | ||
![]() |
bc630fc445 | ||
![]() |
895cf16d33 | ||
![]() |
7f1796b12e | ||
![]() |
abe5c8afab | ||
![]() |
47f79b5269 | ||
![]() |
c9c07eaa15 | ||
![]() |
a490e3af0c | ||
![]() |
64d5b3bdea | ||
![]() |
bf91eaf22f | ||
![]() |
2c3e73b540 | ||
![]() |
830d9dadb9 | ||
![]() |
331d88ebba |
@@ -137,7 +137,11 @@ set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
add_library(device STATIC IMPORTED)
|
||||
set_target_properties(device PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
|
||||
|
||||
add_library(device_trezor STATIC IMPORTED)
|
||||
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
|
||||
|
||||
add_library(multisig STATIC IMPORTED)
|
||||
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
|
||||
@@ -147,6 +151,22 @@ add_library(version STATIC IMPORTED)
|
||||
set_target_properties(version PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
|
||||
|
||||
add_library(net STATIC IMPORTED)
|
||||
set_target_properties(net PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
|
||||
|
||||
add_library(hardforks STATIC IMPORTED)
|
||||
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libhardforks.a)
|
||||
|
||||
add_library(randomx STATIC IMPORTED)
|
||||
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librandomx.a)
|
||||
|
||||
add_library(rpc_base STATIC IMPORTED)
|
||||
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librpc_base.a)
|
||||
|
||||
#############
|
||||
# System
|
||||
#############
|
||||
@@ -166,6 +186,7 @@ target_link_libraries( monerujo
|
||||
mnemonics
|
||||
ringct
|
||||
ringct_basic
|
||||
net
|
||||
common
|
||||
cncrypto
|
||||
blockchain_db
|
||||
@@ -176,8 +197,12 @@ target_link_libraries( monerujo
|
||||
blocks
|
||||
checkpoints
|
||||
device
|
||||
device_trezor
|
||||
multisig
|
||||
version
|
||||
randomx
|
||||
hardforks
|
||||
rpc_base
|
||||
|
||||
boost_chrono
|
||||
boost_date_time
|
||||
|
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 176
|
||||
versionName "1.11.6 'Chernushka'"
|
||||
versionCode 193
|
||||
versionName "1.12.3 'Caerbannog'"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
|
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) 2017-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#if defined(HAVE_MONERUJO)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LedgerFind - find Ledger Device and return it's name
|
||||
* @param buffer - buffer for name of found device
|
||||
* @param len - length of buffer
|
||||
* @return 0 - success
|
||||
* -1 - no device connected / found
|
||||
* -2 - JVM not found
|
||||
*/
|
||||
int LedgerFind(char *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* @brief LedgerExchange - exchange data with Ledger Device
|
||||
* @param command - buffer for data to send
|
||||
* @param cmd_len - length of send 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
|
||||
*/
|
||||
int LedgerExchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "device_io.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace hw {
|
||||
namespace io {
|
||||
class device_io_monerujo: device_io {
|
||||
public:
|
||||
device_io_monerujo() {};
|
||||
~device_io_monerujo() {};
|
||||
|
||||
void init() {};
|
||||
void release() {};
|
||||
|
||||
void connect(void *params) {};
|
||||
void disconnect() {};
|
||||
bool connected() const {return true;}; // monerujo is always connected before it gets here
|
||||
|
||||
// returns number of bytes read or -1 on error
|
||||
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) {
|
||||
return LedgerExchange(command, cmd_len, response, max_resp_len);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif //#if defined(HAVE_MONERUJO)
|
@@ -39,6 +39,7 @@ static jclass class_WalletListener;
|
||||
static jclass class_TransactionInfo;
|
||||
static jclass class_Transfer;
|
||||
static jclass class_Ledger;
|
||||
static jclass class_WalletStatus;
|
||||
|
||||
std::mutex _listenerMutex;
|
||||
|
||||
@@ -61,6 +62,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
|
||||
class_Ledger = static_cast<jclass>(jenv->NewGlobalRef(
|
||||
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
|
||||
class_WalletStatus = static_cast<jclass>(jenv->NewGlobalRef(
|
||||
jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status")));
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
@@ -581,10 +584,25 @@ Java_com_m2049r_xmrwallet_model_Wallet_getStatusJ(JNIEnv *env, jobject instance)
|
||||
return wallet->status();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_getErrorString(JNIEnv *env, jobject instance) {
|
||||
jobject newWalletStatusInstance(JNIEnv *env, int status, const std::string &errorString) {
|
||||
jmethodID init = env->GetMethodID(class_WalletStatus, "<init>",
|
||||
"(ILjava/lang/String;)V");
|
||||
jstring _errorString = env->NewStringUTF(errorString.c_str());
|
||||
jobject instance = env->NewObject(class_WalletStatus, init, status, _errorString);
|
||||
env->DeleteLocalRef(_errorString);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_statusWithErrorString(JNIEnv *env, jobject instance) {
|
||||
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||
return env->NewStringUTF(wallet->errorString().c_str());
|
||||
|
||||
int status;
|
||||
std::string errorString;
|
||||
wallet->statusWithErrorString(status, errorString);
|
||||
|
||||
return newWalletStatusInstance(env, status, errorString);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
@@ -1292,7 +1310,6 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxIdJ(JNIEnv *env, jo
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobject instance) {
|
||||
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);
|
||||
@@ -1378,12 +1395,15 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jclass cl
|
||||
Bitmonero::WalletManagerFactory::setLogLevel(level);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_WalletManager_moneroVersion(JNIEnv *env, jclass clazz) {
|
||||
return env->NewStringUTF(MONERO_VERSION);
|
||||
}
|
||||
|
||||
//
|
||||
// Ledger Stuff
|
||||
//
|
||||
|
||||
#include "device_io_monerujo.hpp"
|
||||
|
||||
/**
|
||||
* @brief LedgerExchange - exchange data with Ledger Device
|
||||
* @param command - buffer for data to send
|
||||
@@ -1417,7 +1437,7 @@ int LedgerExchange(
|
||||
return -1;
|
||||
}
|
||||
jsize len = jenv->GetArrayLength(dataRecv);
|
||||
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cmd_len, len);
|
||||
LOGD("LedgerExchange SCARD_S_SUCCESS %u/%d", cmd_len, len);
|
||||
if (len <= max_resp_len) {
|
||||
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) response);
|
||||
jenv->DeleteLocalRef(dataRecv);
|
||||
|
@@ -54,6 +54,8 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern const char* const MONERO_VERSION; // the actual monero core version
|
||||
|
||||
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||
enum {
|
||||
HASH_SIZE = 32,
|
||||
|
@@ -46,8 +46,12 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
|
||||
for (UsbDevice device : deviceList.values()) {
|
||||
Timber.d("%04X:%04X %s, %s", device.getVendorId(), device.getProductId(), device.getManufacturerName(), device.getProductName());
|
||||
if ((device.getVendorId() == VID) && (device.getProductId() == PID_HID)) {
|
||||
return device;
|
||||
if (device.getVendorId() == VID) {
|
||||
final int deviceProductId = device.getProductId();
|
||||
for (int pid : PID_HIDS) {
|
||||
if (deviceProductId == pid)
|
||||
return device;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -74,7 +78,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||
}
|
||||
|
||||
private static final int VID = 0x2C97;
|
||||
private static final int PID_HID = 0x0001;
|
||||
private static final int[] PID_HIDS = {0x0001, 0x0004};
|
||||
|
||||
private UsbDeviceConnection connection;
|
||||
private UsbInterface dongleInterface;
|
||||
|
@@ -682,8 +682,7 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
if (Helper.preventScreenshot()) {
|
||||
ledgerDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
|
@@ -55,6 +55,8 @@ import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class GenerateReviewFragment extends Fragment {
|
||||
@@ -72,6 +74,7 @@ public class GenerateReviewFragment extends Fragment {
|
||||
private TextView tvWalletPassword;
|
||||
private TextView tvWalletAddress;
|
||||
private TextView tvWalletMnemonic;
|
||||
private TextView tvWalletHeight;
|
||||
private TextView tvWalletViewKey;
|
||||
private TextView tvWalletSpendKey;
|
||||
private ImageButton bCopyAddress;
|
||||
@@ -99,6 +102,7 @@ public class GenerateReviewFragment extends Fragment {
|
||||
tvWalletViewKey = view.findViewById(R.id.tvWalletViewKey);
|
||||
tvWalletSpendKey = view.findViewById(R.id.tvWalletSpendKey);
|
||||
tvWalletMnemonic = view.findViewById(R.id.tvWalletMnemonic);
|
||||
tvWalletHeight = view.findViewById(R.id.tvWalletHeight);
|
||||
bCopyAddress = view.findViewById(R.id.bCopyAddress);
|
||||
bAdvancedInfo = view.findViewById(R.id.bAdvancedInfo);
|
||||
llAdvancedInfo = view.findViewById(R.id.llAdvancedInfo);
|
||||
@@ -188,11 +192,12 @@ public class GenerateReviewFragment extends Fragment {
|
||||
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
|
||||
String name;
|
||||
String address;
|
||||
long height;
|
||||
String seed;
|
||||
String viewKey;
|
||||
String spendKey;
|
||||
boolean isWatchOnly;
|
||||
Wallet.Status status;
|
||||
Wallet.Status walletStatus;
|
||||
|
||||
boolean dialogOpened = false;
|
||||
|
||||
@@ -224,14 +229,15 @@ public class GenerateReviewFragment extends Fragment {
|
||||
closeWallet = true;
|
||||
}
|
||||
name = wallet.getName();
|
||||
status = wallet.getStatus();
|
||||
if (status != Wallet.Status.Status_Ok) {
|
||||
Timber.e(wallet.getErrorString());
|
||||
walletStatus = wallet.getStatus();
|
||||
if (!walletStatus.isOk()) {
|
||||
Timber.e(walletStatus.getErrorString());
|
||||
if (closeWallet) wallet.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
address = wallet.getAddress();
|
||||
height = wallet.getRestoreHeight();
|
||||
seed = wallet.getSeed();
|
||||
switch (wallet.getDeviceType()) {
|
||||
case Device_Ledger:
|
||||
@@ -264,6 +270,7 @@ public class GenerateReviewFragment extends Fragment {
|
||||
llPassword.setVisibility(View.VISIBLE);
|
||||
tvWalletPassword.setText(getPassword());
|
||||
tvWalletAddress.setText(address);
|
||||
tvWalletHeight.setText(NumberFormat.getInstance().format(height));
|
||||
if (!seed.isEmpty()) {
|
||||
llMnemonic.setVisibility(View.VISIBLE);
|
||||
tvWalletMnemonic.setText(seed);
|
||||
@@ -287,10 +294,11 @@ public class GenerateReviewFragment extends Fragment {
|
||||
GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK);
|
||||
} else {
|
||||
// TODO show proper error message and/or end the fragment?
|
||||
tvWalletAddress.setText(status.toString());
|
||||
tvWalletMnemonic.setText(status.toString());
|
||||
tvWalletViewKey.setText(status.toString());
|
||||
tvWalletSpendKey.setText(status.toString());
|
||||
tvWalletAddress.setText(walletStatus.toString());
|
||||
tvWalletHeight.setText(walletStatus.toString());
|
||||
tvWalletMnemonic.setText(walletStatus.toString());
|
||||
tvWalletViewKey.setText(walletStatus.toString());
|
||||
tvWalletSpendKey.setText(walletStatus.toString());
|
||||
}
|
||||
hideProgress();
|
||||
}
|
||||
@@ -414,12 +422,13 @@ public class GenerateReviewFragment extends Fragment {
|
||||
}
|
||||
|
||||
boolean ok = false;
|
||||
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
|
||||
Wallet.Status walletStatus = wallet.getStatus();
|
||||
if (walletStatus.isOk()) {
|
||||
wallet.setPassword(newPassword);
|
||||
wallet.store();
|
||||
ok = true;
|
||||
} else {
|
||||
Timber.e(wallet.getErrorString());
|
||||
Timber.e(walletStatus.getErrorString());
|
||||
}
|
||||
if (closeWallet) wallet.close();
|
||||
return ok;
|
||||
@@ -621,8 +630,8 @@ public class GenerateReviewFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
|
||||
if (Helper.preventScreenshot()) {
|
||||
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
|
@@ -918,6 +918,16 @@ public class LoginActivity extends BaseActivity
|
||||
|
||||
}
|
||||
|
||||
boolean checkAndCloseWallet(Wallet aWallet) {
|
||||
Wallet.Status walletStatus = aWallet.getStatus();
|
||||
if (!walletStatus.isOk()) {
|
||||
Timber.e(walletStatus.getErrorString());
|
||||
toast(walletStatus.getErrorString());
|
||||
}
|
||||
aWallet.close();
|
||||
return walletStatus.isOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGenerate(final String name, final String password) {
|
||||
createWallet(name, password,
|
||||
@@ -929,15 +939,13 @@ public class LoginActivity extends BaseActivity
|
||||
|
||||
@Override
|
||||
public boolean createWallet(File aFile, String password) {
|
||||
NodeInfo currentNode = getNode();
|
||||
// get it from the connected node if we have one, and go back ca. 4 days
|
||||
final long restoreHeight =
|
||||
(currentNode != null) ? currentNode.getHeight() - 2000 : -1;
|
||||
Wallet newWallet = WalletManager.getInstance()
|
||||
.createWallet(aFile, password, MNEMONIC_LANGUAGE);
|
||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||
if (!success) {
|
||||
Timber.e(newWallet.getErrorString());
|
||||
toast(newWallet.getErrorString());
|
||||
}
|
||||
newWallet.close();
|
||||
return success;
|
||||
.createWallet(aFile, password, MNEMONIC_LANGUAGE, restoreHeight);
|
||||
return checkAndCloseWallet(newWallet);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -956,13 +964,7 @@ public class LoginActivity extends BaseActivity
|
||||
public boolean createWallet(File aFile, String password) {
|
||||
Wallet newWallet = WalletManager.getInstance()
|
||||
.recoveryWallet(aFile, password, seed, restoreHeight);
|
||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||
if (!success) {
|
||||
Timber.e(newWallet.getErrorString());
|
||||
toast(newWallet.getErrorString());
|
||||
}
|
||||
newWallet.close();
|
||||
return success;
|
||||
return checkAndCloseWallet(newWallet);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -982,13 +984,7 @@ public class LoginActivity extends BaseActivity
|
||||
Wallet newWallet = WalletManager.getInstance()
|
||||
.createWalletFromDevice(aFile, password,
|
||||
restoreHeight, "Ledger");
|
||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||
if (!success) {
|
||||
Timber.e(newWallet.getErrorString());
|
||||
toast(newWallet.getErrorString());
|
||||
}
|
||||
newWallet.close();
|
||||
return success;
|
||||
return checkAndCloseWallet(newWallet);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1009,13 +1005,7 @@ public class LoginActivity extends BaseActivity
|
||||
Wallet newWallet = WalletManager.getInstance()
|
||||
.createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight,
|
||||
address, viewKey, spendKey);
|
||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||
if (!success) {
|
||||
Timber.e(newWallet.getErrorString());
|
||||
toast(newWallet.getErrorString());
|
||||
}
|
||||
newWallet.close();
|
||||
return success;
|
||||
return checkAndCloseWallet(newWallet);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1370,17 +1360,30 @@ public class LoginActivity extends BaseActivity
|
||||
if (Ledger.ENABLED)
|
||||
try {
|
||||
Ledger.connect(usbManager, usbDevice);
|
||||
registerDetachReceiver();
|
||||
onLedgerAction();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
if (!Ledger.check()) {
|
||||
Ledger.disconnect();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.toast_ledger_start_app, usbDevice.getProductName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
registerDetachReceiver();
|
||||
onLedgerAction();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
|
@@ -439,10 +439,13 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
}
|
||||
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
|
||||
NodeInfo bestNode = nodesToTest.get(0);
|
||||
if (bestNode.isValid())
|
||||
return nodesToTest.get(0);
|
||||
else
|
||||
if (bestNode.isValid()) {
|
||||
activityCallback.setNode(bestNode);
|
||||
return bestNode;
|
||||
} else {
|
||||
activityCallback.setNode(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -450,7 +453,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
if (!isAdded()) return;
|
||||
pbNode.setVisibility(View.INVISIBLE);
|
||||
llNode.setVisibility(View.VISIBLE);
|
||||
activityCallback.setNode(result);
|
||||
if (result != null) {
|
||||
Timber.d("found a good node %s", result.toString());
|
||||
showNode(result);
|
||||
|
@@ -266,6 +266,7 @@ public class NodeFragment extends Fragment
|
||||
seedList.add(new NodeInfo(new InetSocketAddress("198.74.231.92", 18080)));
|
||||
seedList.add(new NodeInfo(new InetSocketAddress("195.154.123.123", 18080)));
|
||||
seedList.add(new NodeInfo(new InetSocketAddress("212.83.172.165", 18080)));
|
||||
seedList.add(new NodeInfo(new InetSocketAddress("192.110.160.146", 18080)));
|
||||
d.seedPeers(seedList);
|
||||
d.awaitTermination(NODES_TO_FIND);
|
||||
}
|
||||
@@ -506,8 +507,8 @@ public class NodeFragment extends Fragment
|
||||
});
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
|
||||
if (Helper.preventScreenshot()) {
|
||||
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,7 @@ import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
@@ -30,8 +31,7 @@ public abstract class SecureActivity extends AppCompatActivity {
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
if (Helper.preventScreenshot()) {
|
||||
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
@@ -628,23 +628,22 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletStarted(final Wallet.ConnectionStatus connStatus) {
|
||||
public void onWalletStarted(final Wallet.Status walletStatus) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
dismissProgressDialog();
|
||||
switch (connStatus) {
|
||||
case ConnectionStatus_Disconnected:
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case ConnectionStatus_WrongVersion:
|
||||
if (walletStatus == null) {
|
||||
// guess what went wrong
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (Wallet.ConnectionStatus.ConnectionStatus_WrongVersion == walletStatus.getConnectionStatus())
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_wrongversion), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case ConnectionStatus_Connected:
|
||||
break;
|
||||
else if (!walletStatus.isOk())
|
||||
Toast.makeText(WalletActivity.this, walletStatus.getErrorString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||
if ((walletStatus == null) || (Wallet.ConnectionStatus.ConnectionStatus_Connected != walletStatus.getConnectionStatus())) {
|
||||
finish();
|
||||
} else {
|
||||
haveWallet = true;
|
||||
|
@@ -235,8 +235,10 @@ public class NodeInfo extends Node {
|
||||
String rpcVersion = json.getString("jsonrpc");
|
||||
if (!RPC_VERSION.equals(rpcVersion))
|
||||
return false;
|
||||
final JSONObject header = json.getJSONObject(
|
||||
"result").getJSONObject("block_header");
|
||||
final JSONObject result = json.getJSONObject("result");
|
||||
if (!result.has("credits")) // introduced in monero v0.15.0
|
||||
return false;
|
||||
final JSONObject header = result.getJSONObject("block_header");
|
||||
height = header.getLong("height");
|
||||
timestamp = header.getLong("timestamp");
|
||||
majorVersion = header.getInt("major_version");
|
||||
|
@@ -28,6 +28,7 @@ import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -76,8 +77,7 @@ public class ProgressDialog extends AlertDialog {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
if (Helper.preventScreenshot()) {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,9 @@ import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
@@ -92,6 +94,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
private View llPaymentId;
|
||||
private TextView tvXmrTo;
|
||||
private View llXmrTo;
|
||||
private ImageButton bPasteAddress;
|
||||
|
||||
private boolean resolvingOA = false;
|
||||
private boolean resolvingPP = false;
|
||||
@@ -137,28 +140,12 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
next = null;
|
||||
} else {
|
||||
// maybe a bip72 or 70 URI
|
||||
String bip70 = PaymentProtocolHelper.getBip70(enteredAddress);
|
||||
final String bip70 = PaymentProtocolHelper.getBip70(enteredAddress);
|
||||
if (bip70 != null) {
|
||||
// looks good - resolve through xmr.to
|
||||
processBip70(bip70);
|
||||
next = null;
|
||||
} else if (checkAddress()) {
|
||||
if (llPaymentId.getVisibility() == View.VISIBLE) {
|
||||
next = etPaymentId;
|
||||
} else {
|
||||
next = etNotes;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next != null) {
|
||||
final View focus = next;
|
||||
etAddress.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
focus.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -173,6 +160,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
Timber.d("isIntegratedAddress");
|
||||
etPaymentId.getEditText().getText().clear();
|
||||
llPaymentId.setVisibility(View.INVISIBLE);
|
||||
etAddress.setError(getString(R.string.info_paymentid_integrated));
|
||||
tvPaymentIdIntegrated.setVisibility(View.VISIBLE);
|
||||
llXmrTo.setVisibility(View.INVISIBLE);
|
||||
sendListener.setMode(SendFragment.Mode.XMR);
|
||||
@@ -197,6 +185,32 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
});
|
||||
|
||||
bPasteAddress = view.findViewById(R.id.bPasteAddress);
|
||||
bPasteAddress.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String clip = Helper.getClipBoardText(getActivity());
|
||||
if (clip == null) return;
|
||||
// clean it up
|
||||
final String address = clip.replaceAll("[^0-9A-Z-a-z]", "");
|
||||
if (Wallet.isAddressValid(address) || BitcoinAddressValidator.validate(address)) {
|
||||
final EditText et = etAddress.getEditText();
|
||||
et.setText(address);
|
||||
et.setSelection(et.getText().length());
|
||||
etAddress.requestFocus();
|
||||
} else {
|
||||
final String bip70 = PaymentProtocolHelper.getBip70(clip);
|
||||
if (bip70 != null) {
|
||||
final EditText et = etAddress.getEditText();
|
||||
et.setText(clip);
|
||||
et.setSelection(et.getText().length());
|
||||
processBip70(bip70);
|
||||
} else
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
etPaymentId = view.findViewById(R.id.etPaymentId);
|
||||
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@@ -230,7 +244,10 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
bPaymentId.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
etPaymentId.getEditText().setText((Wallet.generatePaymentId()));
|
||||
final EditText et = etPaymentId.getEditText();
|
||||
et.setText((Wallet.generatePaymentId()));
|
||||
et.setSelection(et.getText().length());
|
||||
etPaymentId.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -241,7 +258,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
etDummy.requestFocus();
|
||||
Helper.hideKeyboard(getActivity());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -259,7 +275,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
etDummy = view.findViewById(R.id.etDummy);
|
||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
etDummy.requestFocus();
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
View tvNfc = view.findViewById(R.id.tvNfc);
|
||||
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
|
||||
@@ -533,7 +548,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
etDummy.requestFocus();
|
||||
}
|
||||
|
||||
|
@@ -21,8 +21,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
@@ -30,8 +28,7 @@ import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeTextView;
|
||||
import com.m2049r.xmrwallet.widget.NumberPadView;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeEditText;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -59,8 +56,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
|
||||
private TextView tvFunds;
|
||||
private ExchangeTextView evAmount;
|
||||
private View llAmount;
|
||||
private ExchangeEditText etAmount;
|
||||
private View rlSweep;
|
||||
private ImageButton ibSweep;
|
||||
|
||||
@@ -75,12 +71,9 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
View view = inflater.inflate(R.layout.fragment_send_amount, container, false);
|
||||
|
||||
tvFunds = view.findViewById(R.id.tvFunds);
|
||||
|
||||
evAmount = view.findViewById(R.id.evAmount);
|
||||
((NumberPadView) view.findViewById(R.id.numberPad)).setListener(evAmount);
|
||||
|
||||
etAmount = view.findViewById(R.id.etAmount);
|
||||
rlSweep = view.findViewById(R.id.rlSweep);
|
||||
llAmount = view.findViewById(R.id.llAmount);
|
||||
|
||||
view.findViewById(R.id.ivSweep).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -97,8 +90,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
});
|
||||
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
etAmount.requestFocus();
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -107,11 +99,11 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
private void sweepAll(boolean spendAllMode) {
|
||||
if (spendAllMode) {
|
||||
ibSweep.setVisibility(View.INVISIBLE);
|
||||
llAmount.setVisibility(View.GONE);
|
||||
etAmount.setVisibility(View.GONE);
|
||||
rlSweep.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
ibSweep.setVisibility(View.VISIBLE);
|
||||
llAmount.setVisibility(View.VISIBLE);
|
||||
etAmount.setVisibility(View.VISIBLE);
|
||||
rlSweep.setVisibility(View.GONE);
|
||||
}
|
||||
this.spendAllMode = spendAllMode;
|
||||
@@ -124,12 +116,12 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
|
||||
}
|
||||
} else {
|
||||
if (!evAmount.validate(maxFunds)) {
|
||||
if (!etAmount.validate(maxFunds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sendListener != null) {
|
||||
String xmr = evAmount.getAmount();
|
||||
String xmr = etAmount.getAmount();
|
||||
if (xmr != null) {
|
||||
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
||||
} else {
|
||||
@@ -146,7 +138,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
Helper.showKeyboard(getActivity());
|
||||
final long funds = getTotalFunds();
|
||||
maxFunds = 1.0 * funds / 1000000000000L;
|
||||
if (!sendListener.getActivityCallback().isStreetMode()) {
|
||||
@@ -157,10 +149,10 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
getString(R.string.unknown_amount)));
|
||||
}
|
||||
// getAmount is null if exchange is in progress
|
||||
if ((evAmount.getAmount() != null) && evAmount.getAmount().isEmpty()) {
|
||||
if ((etAmount.getAmount() != null) && etAmount.getAmount().isEmpty()) {
|
||||
final BarcodeData data = sendListener.popBarcodeData();
|
||||
if ((data != null) && (data.amount != null)) {
|
||||
evAmount.setAmount(data.amount);
|
||||
etAmount.setAmount(data.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -31,8 +31,7 @@ import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeBtcTextView;
|
||||
import com.m2049r.xmrwallet.widget.NumberPadView;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeBtcEditText;
|
||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
@@ -62,8 +61,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
|
||||
private TextView tvFunds;
|
||||
private ExchangeBtcTextView evAmount;
|
||||
private NumberPadView numberPad;
|
||||
private ExchangeBtcEditText etAmount;
|
||||
|
||||
private TextView tvXmrToParms;
|
||||
private SendProgressView evParams;
|
||||
@@ -86,24 +84,20 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
tvXmrToParms = view.findViewById(R.id.tvXmrToParms);
|
||||
|
||||
evAmount = view.findViewById(R.id.evAmount);
|
||||
numberPad = view.findViewById(R.id.numberPad);
|
||||
numberPad.setListener(evAmount);
|
||||
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
etAmount = view.findViewById(R.id.etAmount);
|
||||
etAmount.requestFocus();
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onValidateFields() {
|
||||
if (!evAmount.validate(maxBtc, minBtc)) {
|
||||
if (!etAmount.validate(maxBtc, minBtc)) {
|
||||
return false;
|
||||
}
|
||||
if (sendListener != null) {
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
String btcString = evAmount.getAmount();
|
||||
String btcString = etAmount.getAmount();
|
||||
if (btcString != null) {
|
||||
try {
|
||||
double btc = Double.parseDouble(btcString);
|
||||
@@ -122,10 +116,12 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
private void setBip70Mode() {
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
if (txDataBtc.getBip70() != null) {
|
||||
numberPad.setVisibility(View.INVISIBLE);
|
||||
if (txDataBtc.getBip70() == null) {
|
||||
etAmount.setEditable(true);
|
||||
Helper.showKeyboard(getActivity());
|
||||
} else {
|
||||
numberPad.setVisibility(View.VISIBLE);
|
||||
etAmount.setEditable(false);
|
||||
Helper.hideKeyboard(getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +137,6 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
final long funds = getTotalFunds();
|
||||
if (!sendListener.getActivityCallback().isStreetMode()) {
|
||||
tvFunds.setText(getString(R.string.send_available,
|
||||
@@ -153,7 +148,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
final BarcodeData data = sendListener.popBarcodeData();
|
||||
if (data != null) {
|
||||
if (data.amount != null) {
|
||||
evAmount.setAmount(data.amount);
|
||||
etAmount.setAmount(data.amount);
|
||||
}
|
||||
}
|
||||
setBip70Mode();
|
||||
@@ -171,7 +166,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
evAmount.setRate(1.0d / orderParameters.getPrice());
|
||||
etAmount.setRate(1.0d / orderParameters.getPrice());
|
||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
df.setMaximumFractionDigits(6);
|
||||
String min = df.format(orderParameters.getLowerLimit());
|
||||
@@ -211,7 +206,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
|
||||
private void processOrderParmsError(final Exception ex) {
|
||||
evAmount.setRate(0);
|
||||
etAmount.setRate(0);
|
||||
orderParameters = null;
|
||||
maxBtc = 0;
|
||||
minBtc = 0;
|
||||
|
@@ -437,8 +437,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
|
||||
if (Helper.preventScreenshot()) {
|
||||
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
|
@@ -324,8 +324,8 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
|
||||
if (Helper.preventScreenshot()) {
|
||||
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
|
@@ -134,6 +134,10 @@ public enum Instruction {
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte getByteValue() {
|
||||
return (byte) (value & 0xFF);
|
||||
}
|
||||
|
||||
private int value;
|
||||
|
||||
Instruction(int value) {
|
||||
|
@@ -27,9 +27,11 @@ import com.btchip.BTChipException;
|
||||
import com.btchip.comm.BTChipTransport;
|
||||
import com.btchip.comm.android.BTChipTransportAndroidHID;
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -40,9 +42,11 @@ public class Ledger {
|
||||
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
||||
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
|
||||
|
||||
private static final byte PROTOCOL_VERSION = 0x02;
|
||||
public static final int SW_OK = 0x9000;
|
||||
public static final int SW_INS_NOT_SUPPORTED = 0x6D00;
|
||||
public static final int OK[] = {SW_OK};
|
||||
public static final int MINIMUM_LEDGER_VERSION = (1 << 16) + (3 << 8) + (1); // 1.3.1
|
||||
|
||||
public static UsbDevice findDevice(UsbManager usbManager) {
|
||||
if (!ENABLED) return null;
|
||||
@@ -89,6 +93,21 @@ public class Ledger {
|
||||
}
|
||||
}
|
||||
|
||||
static public boolean check() {
|
||||
if (Instance == null) return false;
|
||||
byte[] moneroVersion = WalletManager.moneroVersion().getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
try {
|
||||
byte[] resp = Instance.exchangeApduNoOpt(Instruction.INS_RESET, moneroVersion, OK);
|
||||
int deviceVersion = (resp[0] << 16) + (resp[1] << 8) + (resp[2]);
|
||||
if (deviceVersion < MINIMUM_LEDGER_VERSION)
|
||||
return false;
|
||||
} catch (BTChipException ex) { // comm error - probably wrong app started on device
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final private BTChipTransport transport;
|
||||
final private String name;
|
||||
private int lastSW = 0;
|
||||
@@ -112,7 +131,7 @@ public class Ledger {
|
||||
synchronized private byte[] exchangeRaw(byte[] apdu) {
|
||||
if (transport == null)
|
||||
throw new IllegalStateException("No transport (probably closed previously)");
|
||||
Timber.i("exchangeRaw %02x", apdu[1]);
|
||||
Timber.d("exchangeRaw %02x", apdu[1]);
|
||||
Instruction ins = Instruction.fromByte(apdu[1]);
|
||||
if (listener != null) listener.onInstructionSend(ins, apdu);
|
||||
sniffOut(ins, apdu);
|
||||
@@ -120,7 +139,6 @@ public class Ledger {
|
||||
if (listener != null) listener.onInstructionReceive(ins, data);
|
||||
sniffIn(data);
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
private byte[] exchange(byte[] apdu) throws BTChipException {
|
||||
@@ -148,68 +166,19 @@ public class Ledger {
|
||||
throw new BTChipException("Invalid status", lastSW);
|
||||
}
|
||||
|
||||
private byte[] exchangeApdu(byte cla, byte ins, byte p1, byte p2, byte[] data, int acceptedSW[]) throws BTChipException {
|
||||
byte[] apdu = new byte[data.length + 5];
|
||||
apdu[0] = cla;
|
||||
apdu[1] = ins;
|
||||
apdu[2] = p1;
|
||||
apdu[3] = p2;
|
||||
apdu[4] = (byte) (data.length);
|
||||
System.arraycopy(data, 0, apdu, 5, data.length);
|
||||
private byte[] exchangeApduNoOpt(Instruction instruction, byte[] data, int acceptedSW[])
|
||||
throws BTChipException {
|
||||
byte[] apdu = new byte[data.length + 6];
|
||||
apdu[0] = PROTOCOL_VERSION;
|
||||
apdu[1] = instruction.getByteValue();
|
||||
apdu[2] = 0; // p1
|
||||
apdu[3] = 0; // p2
|
||||
apdu[4] = (byte) (data.length + 1); // +1 because the opt byte is part of the data
|
||||
apdu[5] = 0; // opt
|
||||
System.arraycopy(data, 0, apdu, 6, data.length);
|
||||
return exchangeCheck(apdu, acceptedSW);
|
||||
}
|
||||
|
||||
private byte[] exchangeApdu(byte cla, byte ins, byte p1, byte p2, int length, int acceptedSW[]) throws BTChipException {
|
||||
byte[] apdu = new byte[5];
|
||||
apdu[0] = cla;
|
||||
apdu[1] = ins;
|
||||
apdu[2] = p1;
|
||||
apdu[3] = p2;
|
||||
apdu[4] = (byte) (length);
|
||||
return exchangeCheck(apdu, acceptedSW);
|
||||
}
|
||||
|
||||
private byte[] exchangeApduSplit(byte cla, byte ins, byte p1, byte p2, byte[] data, int acceptedSW[]) throws BTChipException {
|
||||
int offset = 0;
|
||||
byte[] result = null;
|
||||
while (offset < data.length) {
|
||||
int blockLength = ((data.length - offset) > 255 ? 255 : data.length - offset);
|
||||
byte[] apdu = new byte[blockLength + 5];
|
||||
apdu[0] = cla;
|
||||
apdu[1] = ins;
|
||||
apdu[2] = p1;
|
||||
apdu[3] = p2;
|
||||
apdu[4] = (byte) (blockLength);
|
||||
System.arraycopy(data, offset, apdu, 5, blockLength);
|
||||
result = exchangeCheck(apdu, acceptedSW);
|
||||
offset += blockLength;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] exchangeApduSplit2(byte cla, byte ins, byte p1, byte p2, byte[] data, byte[] data2, int acceptedSW[]) throws BTChipException {
|
||||
int offset = 0;
|
||||
byte[] result = null;
|
||||
int maxBlockSize = 255 - data2.length;
|
||||
while (offset < data.length) {
|
||||
int blockLength = ((data.length - offset) > maxBlockSize ? maxBlockSize : data.length - offset);
|
||||
boolean lastBlock = ((offset + blockLength) == data.length);
|
||||
byte[] apdu = new byte[blockLength + 5 + (lastBlock ? data2.length : 0)];
|
||||
apdu[0] = cla;
|
||||
apdu[1] = ins;
|
||||
apdu[2] = p1;
|
||||
apdu[3] = p2;
|
||||
apdu[4] = (byte) (blockLength + (lastBlock ? data2.length : 0));
|
||||
System.arraycopy(data, offset, apdu, 5, blockLength);
|
||||
if (lastBlock) {
|
||||
System.arraycopy(data2, 0, apdu, 5 + blockLength, data2.length);
|
||||
}
|
||||
result = exchangeCheck(apdu, acceptedSW);
|
||||
offset += blockLength;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onInstructionSend(Instruction ins, byte[] apdu);
|
||||
|
||||
@@ -251,7 +220,6 @@ public class Ledger {
|
||||
if (ins == Instruction.INS_GET_KEY) {
|
||||
snoopKey = (apdu[2] == 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sniffIn(byte[] data) {
|
||||
|
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.m2049r.xmrwallet.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
|
||||
import java.io.File;
|
||||
@@ -32,6 +35,47 @@ public class Wallet {
|
||||
System.loadLibrary("monerujo");
|
||||
}
|
||||
|
||||
static public class Status {
|
||||
Status(int status, String errorString) {
|
||||
this.status = StatusEnum.values()[status];
|
||||
this.errorString = errorString;
|
||||
}
|
||||
|
||||
final private StatusEnum status;
|
||||
final private String errorString;
|
||||
@Nullable
|
||||
private ConnectionStatus connectionStatus; // optional
|
||||
|
||||
public StatusEnum getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getErrorString() {
|
||||
return errorString;
|
||||
}
|
||||
|
||||
public void setConnectionStatus(@Nullable ConnectionStatus connectionStatus) {
|
||||
this.connectionStatus = connectionStatus;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ConnectionStatus getConnectionStatus() {
|
||||
return connectionStatus;
|
||||
}
|
||||
|
||||
public boolean isOk() {
|
||||
return (getStatus() == StatusEnum.Status_Ok)
|
||||
&& ((getConnectionStatus() == null) ||
|
||||
(getConnectionStatus() == ConnectionStatus.ConnectionStatus_Connected));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "Wallet.Status: (" + status + "/" + errorString + ", " + connectionStatus;
|
||||
}
|
||||
}
|
||||
|
||||
private int accountIndex = 0;
|
||||
|
||||
public int getAccountIndex() {
|
||||
@@ -66,7 +110,7 @@ public class Wallet {
|
||||
Device_Ledger
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
public enum StatusEnum {
|
||||
Status_Ok,
|
||||
Status_Error,
|
||||
Status_Critical
|
||||
@@ -85,12 +129,16 @@ public class Wallet {
|
||||
public native void setSeedLanguage(String language);
|
||||
|
||||
public Status getStatus() {
|
||||
return Wallet.Status.values()[getStatusJ()];
|
||||
return statusWithErrorString();
|
||||
}
|
||||
|
||||
private native int getStatusJ();
|
||||
public Status getFullStatus() {
|
||||
Wallet.Status walletStatus = statusWithErrorString();
|
||||
walletStatus.setConnectionStatus(getConnectionStatus());
|
||||
return walletStatus;
|
||||
}
|
||||
|
||||
public native String getErrorString();
|
||||
private native Status statusWithErrorString();
|
||||
|
||||
public native boolean setPassword(String password);
|
||||
|
||||
|
@@ -91,14 +91,16 @@ public class WalletManager {
|
||||
managedWallet = null;
|
||||
}
|
||||
|
||||
public Wallet createWallet(File aFile, String password, String language) {
|
||||
public Wallet createWallet(File aFile, String password, String language, long height) {
|
||||
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
|
||||
if (wallet.getStatus().isOk()) {
|
||||
// (Re-)Estimate restore height based on what we know
|
||||
long oldHeight = wallet.getRestoreHeight();
|
||||
wallet.setRestoreHeight(RestoreHeight.getInstance().getHeight(new Date()));
|
||||
final long oldHeight = wallet.getRestoreHeight();
|
||||
final long restoreHeight =
|
||||
(height > -1) ? height : RestoreHeight.getInstance().getHeight(new Date());
|
||||
wallet.setRestoreHeight(restoreHeight);
|
||||
Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight());
|
||||
wallet.setPassword(password); // this rewrites the keys file (which contains the restore height)
|
||||
}
|
||||
@@ -285,7 +287,8 @@ public class WalletManager {
|
||||
this.daemonAddress = null;
|
||||
this.daemonUsername = "";
|
||||
this.daemonPassword = "";
|
||||
setDaemonAddressJ("");
|
||||
//setDaemonAddressJ(""); // don't disconnect as monero code blocks for many seconds!
|
||||
//TODO: need to do something about that later
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,4 +354,6 @@ public class WalletManager {
|
||||
static public native void logWarning(String category, String message);
|
||||
|
||||
static public native void logError(String category, String message);
|
||||
|
||||
static public native String moneroVersion();
|
||||
}
|
@@ -31,6 +31,7 @@ import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
@@ -220,7 +221,7 @@ public class WalletService extends Service {
|
||||
|
||||
void onSendTransactionFailed(String error);
|
||||
|
||||
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
|
||||
void onWalletStarted(Wallet.Status walletStatus);
|
||||
|
||||
void onWalletOpen(Wallet.Device device);
|
||||
}
|
||||
@@ -287,9 +288,9 @@ public class WalletService extends Service {
|
||||
if (walletId != null) {
|
||||
showProgress(getString(R.string.status_wallet_loading));
|
||||
showProgress(10);
|
||||
Wallet.ConnectionStatus connStatus = start(walletId, walletPw);
|
||||
if (observer != null) observer.onWalletStarted(connStatus);
|
||||
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||
Wallet.Status walletStatus = start(walletId, walletPw);
|
||||
if (observer != null) observer.onWalletStarted(walletStatus);
|
||||
if ((walletStatus == null) || !walletStatus.isOk()) {
|
||||
errorState = true;
|
||||
stop();
|
||||
}
|
||||
@@ -300,7 +301,7 @@ public class WalletService extends Service {
|
||||
boolean rc = myWallet.store();
|
||||
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
|
||||
if (!rc) {
|
||||
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
|
||||
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
|
||||
}
|
||||
if (observer != null) observer.onWalletStored(rc);
|
||||
} else if (cmd.equals(REQUEST_CMD_TX)) {
|
||||
@@ -362,7 +363,7 @@ public class WalletService extends Service {
|
||||
boolean rc = myWallet.store();
|
||||
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
|
||||
if (!rc) {
|
||||
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
|
||||
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
|
||||
}
|
||||
if (observer != null) observer.onWalletStored(rc);
|
||||
listener.updated = true;
|
||||
@@ -464,7 +465,8 @@ public class WalletService extends Service {
|
||||
return true; // true is important so that onUnbind is also called next time
|
||||
}
|
||||
|
||||
private Wallet.ConnectionStatus start(String walletName, String walletPassword) {
|
||||
@Nullable
|
||||
private Wallet.Status start(String walletName, String walletPassword) {
|
||||
Timber.d("start()");
|
||||
startNotfication();
|
||||
showProgress(getString(R.string.status_wallet_loading));
|
||||
@@ -472,11 +474,11 @@ public class WalletService extends Service {
|
||||
if (listener == null) {
|
||||
Timber.d("start() loadWallet");
|
||||
Wallet aWallet = loadWallet(walletName, walletPassword);
|
||||
Wallet.ConnectionStatus connStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
|
||||
if (aWallet != null) connStatus = aWallet.getConnectionStatus();
|
||||
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||
if (aWallet != null) aWallet.close();
|
||||
return connStatus;
|
||||
if (aWallet == null) return null;
|
||||
Wallet.Status walletStatus = aWallet.getFullStatus();
|
||||
if (!walletStatus.isOk()) {
|
||||
aWallet.close();
|
||||
return walletStatus;
|
||||
}
|
||||
listener = new MyWalletListener();
|
||||
listener.start();
|
||||
@@ -487,7 +489,7 @@ public class WalletService extends Service {
|
||||
// if we try to refresh the history here we get occasional segfaults!
|
||||
// doesnt matter since we update as soon as we get a new block anyway
|
||||
Timber.d("start() done");
|
||||
return Wallet.ConnectionStatus.ConnectionStatus_Connected;
|
||||
return getWallet().getFullStatus();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -532,10 +534,9 @@ public class WalletService extends Service {
|
||||
wallet = walletMgr.openWallet(path, walletPassword);
|
||||
showProgress(60);
|
||||
Timber.d("wallet opened");
|
||||
Wallet.Status status = wallet.getStatus();
|
||||
Timber.d("wallet status is %s", status);
|
||||
if (status != Wallet.Status.Status_Ok) {
|
||||
Timber.d("wallet status is %s", status);
|
||||
Wallet.Status walletStatus = wallet.getStatus();
|
||||
if (!walletStatus.isOk()) {
|
||||
Timber.d("wallet status is %s", walletStatus);
|
||||
WalletManager.getInstance().close(wallet); // TODO close() failed?
|
||||
wallet = null;
|
||||
// TODO what do we do with the progress??
|
||||
|
@@ -21,6 +21,7 @@ import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -281,6 +282,21 @@ public class Helper {
|
||||
clipboardManager.setPrimaryClip(clip);
|
||||
}
|
||||
|
||||
static public String getClipBoardText(Context context) {
|
||||
final ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
try {
|
||||
if (clipboardManager.hasPrimaryClip()
|
||||
&& clipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
|
||||
final ClipData.Item item = clipboardManager.getPrimaryClip().getItemAt(0);
|
||||
return item.getText().toString();
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// if we have don't find a text in the clipboard
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static private Animation ShakeAnimation;
|
||||
|
||||
static public Animation getShakeAnimation(Context context) {
|
||||
@@ -585,8 +601,7 @@ public class Helper {
|
||||
}
|
||||
});
|
||||
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
if (Helper.preventScreenshot()) {
|
||||
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
@@ -626,4 +641,8 @@ public class Helper {
|
||||
StrictMode.setThreadPolicy(currentPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
static public boolean preventScreenshot() {
|
||||
return !(BuildConfig.DEBUG || BuildConfig.FLAVOR_type.equals("alpha"));
|
||||
}
|
||||
}
|
||||
|
@@ -103,6 +103,8 @@ public class RestoreHeight {
|
||||
blockheight.put("2019-03-01", 1781681L);
|
||||
blockheight.put("2019-04-01", 1803081L);
|
||||
blockheight.put("2019-05-01", 1824671L);
|
||||
blockheight.put("2019-06-01", 1847005L);
|
||||
blockheight.put("2019-07-01", 1868590L);
|
||||
}
|
||||
|
||||
public long getHeight(String date) {
|
||||
|
@@ -19,9 +19,12 @@
|
||||
package com.m2049r.xmrwallet.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
@@ -31,8 +34,7 @@ import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ExchangeBtcTextView extends LinearLayout
|
||||
implements NumberPadView.NumberPadListener {
|
||||
public class ExchangeBtcEditText extends LinearLayout {
|
||||
|
||||
String btcAmount = null;
|
||||
String xmrAmount = null;
|
||||
@@ -67,7 +69,7 @@ public class ExchangeBtcTextView extends LinearLayout
|
||||
}
|
||||
|
||||
void shakeAmountField() {
|
||||
tvAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
etAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
}
|
||||
|
||||
void shakeExchangeField() {
|
||||
@@ -86,31 +88,35 @@ public class ExchangeBtcTextView extends LinearLayout
|
||||
|
||||
public void setAmount(String btcAmount) {
|
||||
this.btcAmount = btcAmount;
|
||||
tvAmountA.setText(btcAmount);
|
||||
etAmountA.setText(btcAmount);
|
||||
xmrAmount = null;
|
||||
exchange();
|
||||
}
|
||||
|
||||
public void setEditable(boolean editable) {
|
||||
etAmountA.setEnabled(editable);
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return btcAmount;
|
||||
}
|
||||
|
||||
TextView tvAmountA;
|
||||
EditText etAmountA;
|
||||
TextView tvAmountB;
|
||||
Spinner sCurrencyA;
|
||||
Spinner sCurrencyB;
|
||||
|
||||
public ExchangeBtcTextView(Context context) {
|
||||
public ExchangeBtcEditText(Context context) {
|
||||
super(context);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeBtcTextView(Context context, AttributeSet attrs) {
|
||||
public ExchangeBtcEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeBtcTextView(Context context,
|
||||
public ExchangeBtcEditText(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
@@ -125,13 +131,28 @@ public class ExchangeBtcTextView extends LinearLayout
|
||||
private void initializeViews(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.view_exchange_btc_text, this);
|
||||
inflater.inflate(R.layout.view_exchange_btc_edit, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
tvAmountA = findViewById(R.id.tvAmountA);
|
||||
etAmountA = findViewById(R.id.etAmountA);
|
||||
etAmountA.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
exchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
});
|
||||
tvAmountB = findViewById(R.id.tvAmountB);
|
||||
sCurrencyA = findViewById(R.id.sCurrencyA);
|
||||
sCurrencyB = findViewById(R.id.sCurrencyB);
|
||||
@@ -146,12 +167,14 @@ public class ExchangeBtcTextView extends LinearLayout
|
||||
new String[]{"XMR"});
|
||||
sCurrencyB.setAdapter(xmrAdapter);
|
||||
sCurrencyB.setEnabled(false);
|
||||
etAmountA.setFocusable(true);
|
||||
etAmountA.setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
double xmrBtcRate = 0;
|
||||
|
||||
public void exchange() {
|
||||
btcAmount = tvAmountA.getText().toString();
|
||||
btcAmount = etAmountA.getText().toString();
|
||||
if (!btcAmount.isEmpty() && (xmrBtcRate > 0)) {
|
||||
double xmr = xmrBtcRate * Double.parseDouble(btcAmount);
|
||||
xmrAmount = Helper.getFormattedAmount(xmr, true);
|
||||
@@ -161,38 +184,4 @@ public class ExchangeBtcTextView extends LinearLayout
|
||||
tvAmountB.setText(getResources().getString(R.string.send_amount_btc_xmr, xmrAmount));
|
||||
Timber.d("%s BTC =%f> %s XMR", btcAmount, xmrBtcRate, xmrAmount);
|
||||
}
|
||||
|
||||
// deal with attached numpad
|
||||
@Override
|
||||
public void onDigitPressed(final int digit) {
|
||||
tvAmountA.append(String.valueOf(digit));
|
||||
exchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointPressed() {
|
||||
//TODO locale?
|
||||
if (tvAmountA.getText().toString().indexOf('.') == -1) {
|
||||
if (tvAmountA.getText().toString().isEmpty()) {
|
||||
tvAmountA.append("0");
|
||||
}
|
||||
tvAmountA.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackSpacePressed() {
|
||||
String entry = tvAmountA.getText().toString();
|
||||
int length = entry.length();
|
||||
if (length > 0) {
|
||||
tvAmountA.setText(entry.substring(0, entry.length() - 1));
|
||||
exchange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearAll() {
|
||||
tvAmountA.setText(null);
|
||||
exchange();
|
||||
}
|
||||
}
|
@@ -21,11 +21,13 @@ package com.m2049r.xmrwallet.widget;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
@@ -43,10 +45,7 @@ import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ExchangeTextView extends LinearLayout
|
||||
implements NumberPadView.NumberPadListener {
|
||||
|
||||
private static String MAX = "\u221E";
|
||||
public class ExchangeEditText extends LinearLayout {
|
||||
|
||||
String xmrAmount = null;
|
||||
String notXmrAmount = null;
|
||||
@@ -89,7 +88,7 @@ public class ExchangeTextView extends LinearLayout
|
||||
}
|
||||
|
||||
void shakeAmountField() {
|
||||
tvAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
etAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
}
|
||||
|
||||
void shakeExchangeField() {
|
||||
@@ -99,7 +98,7 @@ public class ExchangeTextView extends LinearLayout
|
||||
public void setAmount(String xmrAmount) {
|
||||
if (xmrAmount != null) {
|
||||
setCurrencyA(0);
|
||||
tvAmountA.setText(xmrAmount);
|
||||
etAmountA.setText(xmrAmount);
|
||||
setXmr(xmrAmount);
|
||||
this.notXmrAmount = null;
|
||||
doExchange();
|
||||
@@ -114,7 +113,7 @@ public class ExchangeTextView extends LinearLayout
|
||||
return xmrAmount;
|
||||
}
|
||||
|
||||
TextView tvAmountA;
|
||||
EditText etAmountA;
|
||||
TextView tvAmountB;
|
||||
Spinner sCurrencyA;
|
||||
Spinner sCurrencyB;
|
||||
@@ -146,17 +145,17 @@ public class ExchangeTextView extends LinearLayout
|
||||
return sCurrencyB.getSelectedItemPosition();
|
||||
}
|
||||
|
||||
public ExchangeTextView(Context context) {
|
||||
public ExchangeEditText(Context context) {
|
||||
super(context);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeTextView(Context context, AttributeSet attrs) {
|
||||
public ExchangeEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeTextView(Context context,
|
||||
public ExchangeEditText(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
@@ -171,13 +170,28 @@ public class ExchangeTextView extends LinearLayout
|
||||
private void initializeViews(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.view_exchange_text, this);
|
||||
inflater.inflate(R.layout.view_exchange_edit, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
tvAmountA = findViewById(R.id.tvAmountA);
|
||||
etAmountA = findViewById(R.id.etAmountA);
|
||||
etAmountA.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
doExchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
});
|
||||
tvAmountB = findViewById(R.id.tvAmountB);
|
||||
sCurrencyA = findViewById(R.id.sCurrencyA);
|
||||
sCurrencyB = findViewById(R.id.sCurrencyB);
|
||||
@@ -317,7 +331,7 @@ public class ExchangeTextView extends LinearLayout
|
||||
|
||||
boolean prepareExchange() {
|
||||
Timber.d("prepareExchange()");
|
||||
String enteredAmount = tvAmountA.getText().toString();
|
||||
String enteredAmount = etAmountA.getText().toString();
|
||||
if (!enteredAmount.isEmpty()) {
|
||||
String cleanAmount = "";
|
||||
if (getCurrencyA() == 0) {
|
||||
@@ -426,37 +440,4 @@ public class ExchangeTextView extends LinearLayout
|
||||
public void setOnFailedExchangeListener(OnFailedExchangeListener listener) {
|
||||
onFailedExchangeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDigitPressed(final int digit) {
|
||||
tvAmountA.append(String.valueOf(digit));
|
||||
doExchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointPressed() {
|
||||
//TODO locale?
|
||||
if (tvAmountA.getText().toString().indexOf('.') == -1) {
|
||||
if (tvAmountA.getText().toString().isEmpty()) {
|
||||
tvAmountA.append("0");
|
||||
}
|
||||
tvAmountA.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackSpacePressed() {
|
||||
String entry = tvAmountA.getText().toString();
|
||||
int length = entry.length();
|
||||
if (length > 0) {
|
||||
tvAmountA.setText(entry.substring(0, entry.length() - 1));
|
||||
doExchange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearAll() {
|
||||
tvAmountA.setText(null);
|
||||
doExchange();
|
||||
}
|
||||
}
|
@@ -48,18 +48,9 @@ import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
// TODO combine this with ExchangeTextView
|
||||
|
||||
public class ExchangeView extends LinearLayout
|
||||
implements NumberPadView.NumberPadListener {
|
||||
|
||||
public void enableSoftKeyboard(final boolean isEnabled) {
|
||||
etAmount.getEditText().setShowSoftInputOnFocus(isEnabled);
|
||||
}
|
||||
|
||||
public boolean focus() {
|
||||
return etAmount.requestFocus();
|
||||
}
|
||||
public class ExchangeView extends LinearLayout {
|
||||
String xmrAmount = null;
|
||||
String notXmrAmount = null;
|
||||
|
||||
public void enable(boolean enable) {
|
||||
etAmount.setEnabled(enable);
|
||||
@@ -67,9 +58,6 @@ public class ExchangeView extends LinearLayout
|
||||
sCurrencyB.setEnabled(enable);
|
||||
}
|
||||
|
||||
String xmrAmount = null;
|
||||
String notXmrAmount = null;
|
||||
|
||||
void setXmr(String xmr) {
|
||||
xmrAmount = xmr;
|
||||
if (onNewAmountListener != null) {
|
||||
@@ -482,31 +470,4 @@ public class ExchangeView extends LinearLayout
|
||||
public void setOnFailedExchangeListener(OnFailedExchangeListener listener) {
|
||||
onFailedExchangeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDigitPressed(final int digit) {
|
||||
etAmount.getEditText().append(String.valueOf(digit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointPressed() {
|
||||
//TODO locale?
|
||||
if (etAmount.getEditText().getText().toString().indexOf('.') == -1) {
|
||||
etAmount.getEditText().append(".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackSpacePressed() {
|
||||
Editable editable = etAmount.getEditText().getText();
|
||||
int length = editable.length();
|
||||
if (length > 0) {
|
||||
editable.delete(length - 1, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearAll() {
|
||||
etAmount.getEditText().getText().clear();
|
||||
}
|
||||
}
|
||||
|
@@ -1,87 +0,0 @@
|
||||
package com.m2049r.xmrwallet.widget;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
public class NumberPadView extends LinearLayout
|
||||
implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(final View view) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("NumberPadListener has to be set, use setListener() to set it.");
|
||||
}
|
||||
switch (view.getId()) {
|
||||
case R.id.numberPadPoint:
|
||||
listener.onPointPressed();
|
||||
break;
|
||||
case R.id.numberPadBackSpace:
|
||||
listener.onBackSpacePressed();
|
||||
break;
|
||||
default:
|
||||
if (view.getTag() != null) {
|
||||
listener.onDigitPressed(Integer.parseInt(view.getTag().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(final View view) {
|
||||
if (view.getId() == R.id.numberPadBackSpace) {
|
||||
listener.onClearAll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setListener(final NumberPadListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public interface NumberPadListener {
|
||||
void onDigitPressed(final int digit);
|
||||
|
||||
void onBackSpacePressed();
|
||||
|
||||
void onPointPressed();
|
||||
|
||||
void onClearAll();
|
||||
}
|
||||
|
||||
private NumberPadListener listener;
|
||||
|
||||
public NumberPadView(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NumberPadView(final Context context,
|
||||
@Nullable final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public NumberPadView(final Context context, @Nullable final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
final View view = View.inflate(context, R.layout.view_number_pad, this);
|
||||
setOrientation(VERTICAL);
|
||||
view.findViewById(R.id.numberPad0).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad1).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad2).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad3).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad4).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad5).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad6).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad7).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad8).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPad9).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPadPoint).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPadBackSpace).setOnClickListener(this);
|
||||
view.findViewById(R.id.numberPadBackSpace).setOnLongClickListener(this);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user