Monerujo for Ledger Nano S (#377)
This commit is contained in:
parent
679bae5f42
commit
bf5ed793b3
|
@ -7,8 +7,8 @@ android {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 102
|
versionCode 111
|
||||||
versionName "1.5.12 'Maximum Nachos'"
|
versionName "1.6.1 'Nano S'"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".XmrWalletApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:name=".XmrWalletApplication"
|
|
||||||
android:theme="@style/MyMaterialTheme">
|
android:theme="@style/MyMaterialTheme">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -28,13 +28,39 @@
|
||||||
android:name=".LoginActivity"
|
android:name=".LoginActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="portrait">
|
android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||||
|
android:resource="@xml/usb_device_filter" />
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!--activity
|
||||||
|
android:name=".util.UsbEventReceiverActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:noHistory="true"
|
||||||
|
android:process=":UsbEventReceiverActivityProcess"
|
||||||
|
android:taskAffinity="com.m2049r.xmrwallet.taskAffinityUsbEventReceiver"
|
||||||
|
android:theme="@style/Theme.Transparent">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||||
|
android:resource="@xml/usb_device_filter" />
|
||||||
|
</activity-->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.WalletService"
|
android:name=".service.WalletService"
|
||||||
android:description="@string/service_description"
|
android:description="@string/service_description"
|
||||||
|
|
|
@ -38,6 +38,7 @@ static jclass class_ArrayList;
|
||||||
static jclass class_WalletListener;
|
static jclass class_WalletListener;
|
||||||
static jclass class_TransactionInfo;
|
static jclass class_TransactionInfo;
|
||||||
static jclass class_Transfer;
|
static jclass class_Transfer;
|
||||||
|
static jclass class_Ledger;
|
||||||
|
|
||||||
std::mutex _listenerMutex;
|
std::mutex _listenerMutex;
|
||||||
|
|
||||||
|
@ -58,6 +59,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||||
jenv->FindClass("com/m2049r/xmrwallet/model/Transfer")));
|
jenv->FindClass("com/m2049r/xmrwallet/model/Transfer")));
|
||||||
class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef(
|
class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
|
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
|
||||||
|
class_Ledger = static_cast<jclass>(jenv->NewGlobalRef(
|
||||||
|
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -353,6 +356,39 @@ Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env,
|
||||||
return reinterpret_cast<jlong>(wallet);
|
return reinterpret_cast<jlong>(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0;
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromDeviceJ(JNIEnv *env, jobject instance,
|
||||||
|
jstring path,
|
||||||
|
jstring password,
|
||||||
|
jint networkType,
|
||||||
|
jstring deviceName,
|
||||||
|
jlong restoreHeight,
|
||||||
|
jstring subaddressLookahead) {
|
||||||
|
const char *_path = env->GetStringUTFChars(path, NULL);
|
||||||
|
const char *_password = env->GetStringUTFChars(password, NULL);
|
||||||
|
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
|
||||||
|
const char *_deviceName = env->GetStringUTFChars(deviceName, NULL);
|
||||||
|
const char *_subaddressLookahead = env->GetStringUTFChars(subaddressLookahead, NULL);
|
||||||
|
|
||||||
|
Bitmonero::Wallet *wallet =
|
||||||
|
Bitmonero::WalletManagerFactory::getWalletManager()->createWalletFromDevice(
|
||||||
|
std::string(_path),
|
||||||
|
std::string(_password),
|
||||||
|
_networkType,
|
||||||
|
std::string(_deviceName),
|
||||||
|
(uint64_t) restoreHeight,
|
||||||
|
std::string(_subaddressLookahead));
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(path, _path);
|
||||||
|
env->ReleaseStringUTFChars(password, _password);
|
||||||
|
env->ReleaseStringUTFChars(deviceName, _deviceName);
|
||||||
|
env->ReleaseStringUTFChars(subaddressLookahead, _subaddressLookahead);
|
||||||
|
return reinterpret_cast<jlong>(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject instance,
|
Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject instance,
|
||||||
jstring path) {
|
jstring path) {
|
||||||
|
@ -378,6 +414,20 @@ Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env,
|
||||||
return static_cast<jboolean>(passwordOk);
|
return static_cast<jboolean>(passwordOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//virtual int queryWalletHardware(const std::string &keys_file_name, const std::string &password) const = 0;
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_com_m2049r_xmrwallet_model_WalletManager_queryWalletHardware(JNIEnv *env, jobject instance,
|
||||||
|
jstring keys_file_name,
|
||||||
|
jstring password) {
|
||||||
|
const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, NULL);
|
||||||
|
const char *_password = env->GetStringUTFChars(password, NULL);
|
||||||
|
int hardwareId =
|
||||||
|
Bitmonero::WalletManagerFactory::getWalletManager()->
|
||||||
|
queryWalletHardware(std::string(_keys_file_name), std::string(_password));
|
||||||
|
env->ReleaseStringUTFChars(keys_file_name, _keys_file_name);
|
||||||
|
env->ReleaseStringUTFChars(password, _password);
|
||||||
|
return static_cast<jint>(hardwareId);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL
|
JNIEXPORT jobject JNICALL
|
||||||
Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance,
|
Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance,
|
||||||
|
@ -712,6 +762,13 @@ Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject insta
|
||||||
return static_cast<jboolean>(wallet->synchronized());
|
return static_cast<jboolean>(wallet->synchronized());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_com_m2049r_xmrwallet_model_Wallet_isKeyOnDevice(JNIEnv *env, jobject instance) {
|
||||||
|
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||||
|
bool key_on_device = wallet->isKeyOnDevice();
|
||||||
|
return static_cast<jboolean>(key_on_device);
|
||||||
|
}
|
||||||
|
|
||||||
//void cn_slow_hash(const void *data, size_t length, char *hash); // from crypto/hash-ops.h
|
//void cn_slow_hash(const void *data, size_t length, char *hash); // from crypto/hash-ops.h
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_m2049r_xmrwallet_util_KeyStoreHelper_slowHash(JNIEnv *env, jobject clazz,
|
Java_com_m2049r_xmrwallet_util_KeyStoreHelper_slowHash(JNIEnv *env, jobject clazz,
|
||||||
|
@ -1309,6 +1366,96 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jobject i
|
||||||
Bitmonero::WalletManagerFactory::setLogLevel(level);
|
Bitmonero::WalletManagerFactory::setLogLevel(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ledger Stuff
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "monerujo_ledger.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LedgerExchange - exchange data with Ledger Device
|
||||||
|
* @param pbSendBuffer - buffer for data to send
|
||||||
|
* @param cbSendLength - length of send buffer
|
||||||
|
* @param pbRecvBuffer - buffer for received data
|
||||||
|
* @param pcbRecvLength - pointer to size of receive buffer
|
||||||
|
* gets set with length of received data on successful return
|
||||||
|
* @return SCARD_S_SUCCESS - success
|
||||||
|
* SCARD_E_NO_READERS_AVAILABLE - no device connected / found
|
||||||
|
* SCARD_E_INSUFFICIENT_BUFFER - pbRecvBuffer is too small for the response
|
||||||
|
*/
|
||||||
|
LONG LedgerExchange(
|
||||||
|
LPCBYTE pbSendBuffer,
|
||||||
|
DWORD cbSendLength,
|
||||||
|
LPBYTE pbRecvBuffer,
|
||||||
|
LPDWORD pcbRecvLength) {
|
||||||
|
LOGD("LedgerExchange");
|
||||||
|
JNIEnv *jenv;
|
||||||
|
int envStat = attachJVM(&jenv);
|
||||||
|
if (envStat == JNI_ERR) return -1;
|
||||||
|
|
||||||
|
jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B");
|
||||||
|
|
||||||
|
jsize sendLen = static_cast<jsize>(cbSendLength);
|
||||||
|
jbyteArray dataSend = jenv->NewByteArray(sendLen);
|
||||||
|
jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) pbSendBuffer);
|
||||||
|
jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod,
|
||||||
|
dataSend);
|
||||||
|
jenv->DeleteLocalRef(dataSend);
|
||||||
|
if (dataRecv == nullptr) {
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE");
|
||||||
|
return SCARD_E_NO_READERS_AVAILABLE;
|
||||||
|
}
|
||||||
|
jsize len = jenv->GetArrayLength(dataRecv);
|
||||||
|
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cbSendLength, len);
|
||||||
|
if (len <= *pcbRecvLength) {
|
||||||
|
*pcbRecvLength = static_cast<DWORD>(len);
|
||||||
|
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) pbRecvBuffer);
|
||||||
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
return SCARD_S_SUCCESS;
|
||||||
|
} else {
|
||||||
|
jenv->DeleteLocalRef(dataRecv);
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER");
|
||||||
|
return SCARD_E_INSUFFICIENT_BUFFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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) {
|
||||||
|
LOGD("LedgerName");
|
||||||
|
JNIEnv *jenv;
|
||||||
|
int envStat = attachJVM(&jenv);
|
||||||
|
if (envStat == JNI_ERR) return -2;
|
||||||
|
|
||||||
|
jmethodID nameMethod = jenv->GetStaticMethodID(class_Ledger, "Name", "()Ljava/lang/String;");
|
||||||
|
jstring name = (jstring) jenv->CallStaticObjectMethod(class_Ledger, nameMethod);
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
if (name != nullptr) {
|
||||||
|
const char *_name = jenv->GetStringUTFChars(name, NULL);
|
||||||
|
strncpy(buffer, _name, len);
|
||||||
|
jenv->ReleaseStringUTFChars(name, _name);
|
||||||
|
buffer[len - 1] = 0; // terminate in case _name is bigger
|
||||||
|
ret = 0;
|
||||||
|
LOGD("LedgerName is %s", buffer);
|
||||||
|
} else {
|
||||||
|
buffer[0] = 0;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
detachJVM(jenv, envStat);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2018 m2049r
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XMRWALLET_LEDGER_H
|
||||||
|
#define XMRWALLET_LEDGER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
|
||||||
|
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
|
||||||
|
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */
|
||||||
|
|
||||||
|
typedef long LONG;
|
||||||
|
typedef unsigned long DWORD;
|
||||||
|
typedef DWORD *LPDWORD;
|
||||||
|
typedef unsigned char BYTE;
|
||||||
|
typedef BYTE *LPBYTE;
|
||||||
|
typedef const BYTE *LPCBYTE;
|
||||||
|
|
||||||
|
typedef char CHAR;
|
||||||
|
typedef CHAR *LPSTR;
|
||||||
|
|
||||||
|
int LedgerFind(char *buffer, size_t len);
|
||||||
|
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //XMRWALLET_LEDGER_H
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
*
|
||||||
|
* 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.btchip;
|
||||||
|
|
||||||
|
public class BTChipException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5512803003827126405L;
|
||||||
|
|
||||||
|
public BTChipException(String reason) {
|
||||||
|
super(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BTChipException(String reason, Throwable cause) {
|
||||||
|
super(reason, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BTChipException(String reason, int sw) {
|
||||||
|
super(reason);
|
||||||
|
this.sw = sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSW() {
|
||||||
|
return sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (sw == 0) {
|
||||||
|
return "BTChip Exception : " + getMessage();
|
||||||
|
} else {
|
||||||
|
return "BTChip Exception : " + getMessage() + " " + Integer.toHexString(sw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int sw;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
* (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.btchip.comm;
|
||||||
|
|
||||||
|
import com.btchip.BTChipException;
|
||||||
|
|
||||||
|
public interface BTChipTransport {
|
||||||
|
public byte[] exchange(byte[] command);
|
||||||
|
|
||||||
|
public void close();
|
||||||
|
|
||||||
|
public void setDebug(boolean debugFlag);
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
* (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.btchip.comm;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
public class LedgerHelper {
|
||||||
|
|
||||||
|
private static final int TAG_APDU = 0x05;
|
||||||
|
|
||||||
|
public static byte[] wrapCommandAPDU(int channel, byte[] command, int packetSize) {
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
|
if (packetSize < 3) {
|
||||||
|
throw new IllegalArgumentException("Can't handle Ledger framing with less than 3 bytes for the report");
|
||||||
|
}
|
||||||
|
int sequenceIdx = 0;
|
||||||
|
int offset = 0;
|
||||||
|
output.write(channel >> 8);
|
||||||
|
output.write(channel);
|
||||||
|
output.write(TAG_APDU);
|
||||||
|
output.write(sequenceIdx >> 8);
|
||||||
|
output.write(sequenceIdx);
|
||||||
|
sequenceIdx++;
|
||||||
|
output.write(command.length >> 8);
|
||||||
|
output.write(command.length);
|
||||||
|
int blockSize = (command.length > packetSize - 7 ? packetSize - 7 : command.length);
|
||||||
|
output.write(command, offset, blockSize);
|
||||||
|
offset += blockSize;
|
||||||
|
while (offset != command.length) {
|
||||||
|
output.write(channel >> 8);
|
||||||
|
output.write(channel);
|
||||||
|
output.write(TAG_APDU);
|
||||||
|
output.write(sequenceIdx >> 8);
|
||||||
|
output.write(sequenceIdx);
|
||||||
|
sequenceIdx++;
|
||||||
|
blockSize = (command.length - offset > packetSize - 5 ? packetSize - 5 : command.length - offset);
|
||||||
|
output.write(command, offset, blockSize);
|
||||||
|
offset += blockSize;
|
||||||
|
}
|
||||||
|
if ((output.size() % packetSize) != 0) {
|
||||||
|
byte[] padding = new byte[packetSize - (output.size() % packetSize)];
|
||||||
|
output.write(padding, 0, padding.length);
|
||||||
|
}
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] unwrapResponseAPDU(int channel, byte[] data, int packetSize) {
|
||||||
|
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||||
|
int offset = 0;
|
||||||
|
int responseLength;
|
||||||
|
int sequenceIdx = 0;
|
||||||
|
if ((data == null) || (data.length < 7 + 5)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (data[offset++] != (channel >> 8)) {
|
||||||
|
throw new IllegalArgumentException("Invalid channel");
|
||||||
|
}
|
||||||
|
if (data[offset++] != (channel & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Invalid channel");
|
||||||
|
}
|
||||||
|
if (data[offset++] != TAG_APDU) {
|
||||||
|
throw new IllegalArgumentException("Invalid tag");
|
||||||
|
}
|
||||||
|
if (data[offset++] != 0x00) {
|
||||||
|
throw new IllegalArgumentException("Invalid sequence");
|
||||||
|
}
|
||||||
|
if (data[offset++] != 0x00) {
|
||||||
|
throw new IllegalArgumentException("Invalid sequence");
|
||||||
|
}
|
||||||
|
responseLength = ((data[offset++] & 0xff) << 8);
|
||||||
|
responseLength |= (data[offset++] & 0xff);
|
||||||
|
if (data.length < 7 + responseLength) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int blockSize = (responseLength > packetSize - 7 ? packetSize - 7 : responseLength);
|
||||||
|
response.write(data, offset, blockSize);
|
||||||
|
offset += blockSize;
|
||||||
|
while (response.size() != responseLength) {
|
||||||
|
sequenceIdx++;
|
||||||
|
if (offset == data.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (data[offset++] != (channel >> 8)) {
|
||||||
|
throw new IllegalArgumentException("Invalid channel");
|
||||||
|
}
|
||||||
|
if (data[offset++] != (channel & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Invalid channel");
|
||||||
|
}
|
||||||
|
if (data[offset++] != TAG_APDU) {
|
||||||
|
throw new IllegalArgumentException("Invalid tag");
|
||||||
|
}
|
||||||
|
if (data[offset++] != (sequenceIdx >> 8)) {
|
||||||
|
throw new IllegalArgumentException("Invalid sequence");
|
||||||
|
}
|
||||||
|
if (data[offset++] != (sequenceIdx & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Invalid sequence");
|
||||||
|
}
|
||||||
|
blockSize = (responseLength - response.size() > packetSize - 5 ? packetSize - 5 : responseLength - response.size());
|
||||||
|
if (blockSize > data.length - offset) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
response.write(data, offset, blockSize);
|
||||||
|
offset += blockSize;
|
||||||
|
}
|
||||||
|
return response.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
* (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.btchip.comm.android;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbConstants;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.hardware.usb.UsbDeviceConnection;
|
||||||
|
import android.hardware.usb.UsbEndpoint;
|
||||||
|
import android.hardware.usb.UsbInterface;
|
||||||
|
import android.hardware.usb.UsbManager;
|
||||||
|
import android.hardware.usb.UsbRequest;
|
||||||
|
|
||||||
|
import com.btchip.BTChipException;
|
||||||
|
import com.btchip.comm.BTChipTransport;
|
||||||
|
import com.btchip.comm.LedgerHelper;
|
||||||
|
import com.btchip.utils.Dump;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||||
|
|
||||||
|
public static UsbDevice getDevice(UsbManager manager) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BTChipTransport open(UsbManager manager, UsbDevice device) throws IOException {
|
||||||
|
UsbDeviceConnection connection = manager.openDevice(device);
|
||||||
|
if (connection == null) throw new IOException("Device not connected");
|
||||||
|
// Must only be called once permission is granted (see http://developer.android.com/reference/android/hardware/usb/UsbManager.html)
|
||||||
|
// Important if enumerating, rather than being awaken by the intent notification
|
||||||
|
UsbInterface dongleInterface = device.getInterface(0);
|
||||||
|
UsbEndpoint in = null;
|
||||||
|
UsbEndpoint out = null;
|
||||||
|
for (int i = 0; i < dongleInterface.getEndpointCount(); i++) {
|
||||||
|
UsbEndpoint tmpEndpoint = dongleInterface.getEndpoint(i);
|
||||||
|
if (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
|
||||||
|
in = tmpEndpoint;
|
||||||
|
} else {
|
||||||
|
out = tmpEndpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.claimInterface(dongleInterface, true);
|
||||||
|
return new BTChipTransportAndroidHID(connection, dongleInterface, in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int VID = 0x2C97;
|
||||||
|
private static final int PID_HID = 0x0001;
|
||||||
|
|
||||||
|
private UsbDeviceConnection connection;
|
||||||
|
private UsbInterface dongleInterface;
|
||||||
|
private UsbEndpoint in;
|
||||||
|
private UsbEndpoint out;
|
||||||
|
private byte transferBuffer[];
|
||||||
|
private boolean debug;
|
||||||
|
|
||||||
|
public BTChipTransportAndroidHID(UsbDeviceConnection connection, UsbInterface dongleInterface, UsbEndpoint in, UsbEndpoint out) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.dongleInterface = dongleInterface;
|
||||||
|
this.in = in;
|
||||||
|
this.out = out;
|
||||||
|
transferBuffer = new byte[HID_BUFFER_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] exchange(byte[] command) {
|
||||||
|
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||||
|
byte[] responseData = null;
|
||||||
|
int offset = 0;
|
||||||
|
if (debug) {
|
||||||
|
Timber.d("=> %s", Dump.dump(command));
|
||||||
|
}
|
||||||
|
command = LedgerHelper.wrapCommandAPDU(LEDGER_DEFAULT_CHANNEL, command, HID_BUFFER_SIZE);
|
||||||
|
UsbRequest requestOut = new UsbRequest();
|
||||||
|
requestOut.initialize(connection, out);
|
||||||
|
while (offset != command.length) {
|
||||||
|
int blockSize = (command.length - offset > HID_BUFFER_SIZE ? HID_BUFFER_SIZE : command.length - offset);
|
||||||
|
System.arraycopy(command, offset, transferBuffer, 0, blockSize);
|
||||||
|
requestOut.queue(ByteBuffer.wrap(transferBuffer), HID_BUFFER_SIZE);
|
||||||
|
connection.requestWait();
|
||||||
|
offset += blockSize;
|
||||||
|
}
|
||||||
|
requestOut.close();
|
||||||
|
ByteBuffer responseBuffer = ByteBuffer.allocate(HID_BUFFER_SIZE);
|
||||||
|
UsbRequest requestIn = new UsbRequest();
|
||||||
|
requestIn.initialize(connection, in);
|
||||||
|
while ((responseData = LedgerHelper.unwrapResponseAPDU(LEDGER_DEFAULT_CHANNEL, response.toByteArray(), HID_BUFFER_SIZE)) == null) {
|
||||||
|
responseBuffer.clear();
|
||||||
|
requestIn.queue(responseBuffer, HID_BUFFER_SIZE);
|
||||||
|
connection.requestWait();
|
||||||
|
responseBuffer.rewind();
|
||||||
|
responseBuffer.get(transferBuffer, 0, HID_BUFFER_SIZE);
|
||||||
|
response.write(transferBuffer, 0, HID_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
requestIn.close();
|
||||||
|
if (debug) {
|
||||||
|
Timber.d("<= %s", Dump.dump(responseData));
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
connection.releaseInterface(dongleInterface);
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDebug(boolean debugFlag) {
|
||||||
|
this.debug = debugFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int HID_BUFFER_SIZE = 64;
|
||||||
|
private static final int LEDGER_DEFAULT_CHANNEL = 1;
|
||||||
|
private static final int SW1_DATA_AVAILABLE = 0x61;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
*
|
||||||
|
* 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.btchip.utils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
public class Dump {
|
||||||
|
|
||||||
|
public static String dump(byte[] buffer, int offset, int length) {
|
||||||
|
String result = "";
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
String temp = Integer.toHexString((buffer[offset + i]) & 0xff);
|
||||||
|
if (temp.length() < 2) {
|
||||||
|
temp = "0" + temp;
|
||||||
|
}
|
||||||
|
result += temp;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dump(byte[] buffer) {
|
||||||
|
return dump(buffer, 0, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hexToBin(String src) {
|
||||||
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
|
int i = 0;
|
||||||
|
while (i < src.length()) {
|
||||||
|
char x = src.charAt(i);
|
||||||
|
if (!((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'))) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result.write(Integer.valueOf("" + src.charAt(i) + src.charAt(i + 1), 16));
|
||||||
|
i += 2;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
||||||
|
import com.m2049r.xmrwallet.ledger.Ledger;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class BaseActivity extends SecureActivity implements GenerateReviewFragment.ProgressListener {
|
||||||
|
|
||||||
|
ProgressDialog progressDialog = null;
|
||||||
|
|
||||||
|
private class SimpleProgressDialog extends ProgressDialog {
|
||||||
|
|
||||||
|
SimpleProgressDialog(Context context, int msgId) {
|
||||||
|
super(context);
|
||||||
|
setCancelable(false);
|
||||||
|
setMessage(context.getString(msgId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
// prevent back button
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showProgressDialog(int msgId) {
|
||||||
|
showProgressDialog(msgId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showProgressDialog(int msgId, long delay) {
|
||||||
|
dismissProgressDialog(); // just in case
|
||||||
|
progressDialog = new SimpleProgressDialog(BaseActivity.this, msgId);
|
||||||
|
if (delay > 0) {
|
||||||
|
Handler handler = new Handler();
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
if (progressDialog != null) progressDialog.show();
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
|
} else {
|
||||||
|
progressDialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showLedgerProgressDialog(int mode) {
|
||||||
|
dismissProgressDialog(); // just in case
|
||||||
|
progressDialog = new LedgerProgressDialog(BaseActivity.this, mode);
|
||||||
|
Ledger.setListener((Ledger.Listener) progressDialog);
|
||||||
|
progressDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dismissProgressDialog() {
|
||||||
|
if (progressDialog == null) return; // nothing to do
|
||||||
|
if (progressDialog instanceof Ledger.Listener) {
|
||||||
|
Ledger.unsetListener((Ledger.Listener) progressDialog);
|
||||||
|
}
|
||||||
|
if (progressDialog.isShowing()) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
}
|
||||||
|
progressDialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int RELEASE_WAKE_LOCK_DELAY = 5000; // millisconds
|
||||||
|
|
||||||
|
private PowerManager.WakeLock wl = null;
|
||||||
|
|
||||||
|
void acquireWakeLock() {
|
||||||
|
if ((wl != null) && wl.isHeld()) return;
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
|
this.wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, getString(R.string.app_name));
|
||||||
|
try {
|
||||||
|
wl.acquire();
|
||||||
|
Timber.d("WakeLock acquired");
|
||||||
|
} catch (SecurityException ex) {
|
||||||
|
Timber.w("WakeLock NOT acquired: %s", ex.getLocalizedMessage());
|
||||||
|
wl = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseWakeLock(int delayMillis) {
|
||||||
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
releaseWakeLock();
|
||||||
|
}
|
||||||
|
}, delayMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseWakeLock() {
|
||||||
|
if ((wl == null) || !wl.isHeld()) return;
|
||||||
|
wl.release();
|
||||||
|
wl = null;
|
||||||
|
Timber.d("WakeLock released");
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,7 @@ public class GenerateFragment extends Fragment {
|
||||||
static final String TYPE_NEW = "new";
|
static final String TYPE_NEW = "new";
|
||||||
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_VIEWONLY = "view";
|
static final String TYPE_VIEWONLY = "view";
|
||||||
|
|
||||||
private TextInputLayout etWalletName;
|
private TextInputLayout etWalletName;
|
||||||
|
@ -190,6 +191,17 @@ public class GenerateFragment extends Fragment {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (type.equals(TYPE_LEDGER)) {
|
||||||
|
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||||
|
etWalletRestoreHeight.requestFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (type.equals(TYPE_SEED)) {
|
} else if (type.equals(TYPE_SEED)) {
|
||||||
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
@ -485,6 +497,12 @@ public class GenerateFragment extends Fragment {
|
||||||
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
|
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
|
||||||
}
|
}
|
||||||
activityCallback.onGenerate(name, crazyPass, seed, height);
|
activityCallback.onGenerate(name, crazyPass, seed, height);
|
||||||
|
} else if (type.equals(TYPE_LEDGER)) {
|
||||||
|
bGenerate.setEnabled(false);
|
||||||
|
if (fingerprintAuthAllowed) {
|
||||||
|
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
|
||||||
|
}
|
||||||
|
activityCallback.onGenerateLedger(name, crazyPass, height);
|
||||||
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
|
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
|
||||||
if (checkAddress() && checkViewKey() && checkSpendKey()) {
|
if (checkAddress() && checkViewKey() && checkSpendKey()) {
|
||||||
bGenerate.setEnabled(false);
|
bGenerate.setEnabled(false);
|
||||||
|
@ -523,6 +541,8 @@ public class GenerateFragment extends Fragment {
|
||||||
return getString(R.string.generate_wallet_type_new);
|
return getString(R.string.generate_wallet_type_new);
|
||||||
case TYPE_SEED:
|
case TYPE_SEED:
|
||||||
return getString(R.string.generate_wallet_type_seed);
|
return getString(R.string.generate_wallet_type_seed);
|
||||||
|
case TYPE_LEDGER:
|
||||||
|
return getString(R.string.generate_wallet_type_ledger);
|
||||||
case TYPE_VIEWONLY:
|
case TYPE_VIEWONLY:
|
||||||
return getString(R.string.generate_wallet_type_view);
|
return getString(R.string.generate_wallet_type_view);
|
||||||
default:
|
default:
|
||||||
|
@ -540,6 +560,8 @@ 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 setTitle(String title);
|
void setTitle(String title);
|
||||||
|
|
||||||
void setToolbarButton(int type);
|
void setToolbarButton(int type);
|
||||||
|
@ -575,6 +597,9 @@ public class GenerateFragment extends Fragment {
|
||||||
case TYPE_SEED:
|
case TYPE_SEED:
|
||||||
inflater.inflate(R.menu.create_wallet_seed, menu);
|
inflater.inflate(R.menu.create_wallet_seed, menu);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_LEDGER:
|
||||||
|
inflater.inflate(R.menu.create_wallet_ledger, 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;
|
||||||
|
|
|
@ -43,6 +43,8 @@ import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.ledger.Ledger;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
@ -52,8 +54,6 @@ import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||||
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class GenerateReviewFragment extends Fragment {
|
public class GenerateReviewFragment extends Fragment {
|
||||||
|
@ -76,6 +76,9 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
private ImageButton bCopyAddress;
|
private ImageButton bCopyAddress;
|
||||||
private LinearLayout llAdvancedInfo;
|
private LinearLayout llAdvancedInfo;
|
||||||
private LinearLayout llPassword;
|
private LinearLayout llPassword;
|
||||||
|
private LinearLayout llMnemonic;
|
||||||
|
private LinearLayout llSpendKey;
|
||||||
|
private LinearLayout llViewKey;
|
||||||
private Button bAdvancedInfo;
|
private Button bAdvancedInfo;
|
||||||
private Button bAccept;
|
private Button bAccept;
|
||||||
|
|
||||||
|
@ -99,6 +102,9 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
bAdvancedInfo = (Button) view.findViewById(R.id.bAdvancedInfo);
|
bAdvancedInfo = (Button) view.findViewById(R.id.bAdvancedInfo);
|
||||||
llAdvancedInfo = (LinearLayout) view.findViewById(R.id.llAdvancedInfo);
|
llAdvancedInfo = (LinearLayout) view.findViewById(R.id.llAdvancedInfo);
|
||||||
llPassword = (LinearLayout) view.findViewById(R.id.llPassword);
|
llPassword = (LinearLayout) view.findViewById(R.id.llPassword);
|
||||||
|
llMnemonic = (LinearLayout) view.findViewById(R.id.llMnemonic);
|
||||||
|
llSpendKey = (LinearLayout) view.findViewById(R.id.llSpendKey);
|
||||||
|
llViewKey = (LinearLayout) view.findViewById(R.id.llViewKey);
|
||||||
|
|
||||||
bAccept = (Button) view.findViewById(R.id.bAccept);
|
bAccept = (Button) view.findViewById(R.id.bAccept);
|
||||||
|
|
||||||
|
@ -142,7 +148,6 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
void showDetails() {
|
void showDetails() {
|
||||||
showProgress();
|
|
||||||
tvWalletPassword.setText(null);
|
tvWalletPassword.setText(null);
|
||||||
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, walletPath);
|
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, walletPath);
|
||||||
}
|
}
|
||||||
|
@ -188,6 +193,20 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
boolean isWatchOnly;
|
boolean isWatchOnly;
|
||||||
Wallet.Status status;
|
Wallet.Status status;
|
||||||
|
|
||||||
|
boolean dialogOpened = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
showProgress();
|
||||||
|
if ((walletPath != null)
|
||||||
|
&& (WalletManager.getInstance().queryWalletHardware(walletPath + ".keys", getPassword()) == 1)
|
||||||
|
&& (progressCallback != null)) {
|
||||||
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
||||||
|
dialogOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(String... params) {
|
protected Boolean doInBackground(String... params) {
|
||||||
if (params.length != 1) return false;
|
if (params.length != 1) return false;
|
||||||
|
@ -212,7 +231,11 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
|
|
||||||
address = wallet.getAddress();
|
address = wallet.getAddress();
|
||||||
seed = wallet.getSeed();
|
seed = wallet.getSeed();
|
||||||
|
if (wallet.isKeyOnDevice()) {
|
||||||
|
viewKey = Ledger.Key();
|
||||||
|
} else {
|
||||||
viewKey = wallet.getSecretViewKey();
|
viewKey = wallet.getSecretViewKey();
|
||||||
|
}
|
||||||
spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey();
|
spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey();
|
||||||
isWatchOnly = wallet.isWatchOnly();
|
isWatchOnly = wallet.isWatchOnly();
|
||||||
if (closeWallet) wallet.close();
|
if (closeWallet) wallet.close();
|
||||||
|
@ -222,6 +245,8 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
|
if (dialogOpened)
|
||||||
|
progressCallback.dismissProgressDialog();
|
||||||
if (!isAdded()) return; // never mind
|
if (!isAdded()) return; // never mind
|
||||||
walletName = name;
|
walletName = name;
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -232,10 +257,22 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
llPassword.setVisibility(View.VISIBLE);
|
llPassword.setVisibility(View.VISIBLE);
|
||||||
tvWalletPassword.setText(getPassword());
|
tvWalletPassword.setText(getPassword());
|
||||||
tvWalletAddress.setText(address);
|
tvWalletAddress.setText(address);
|
||||||
|
if (!seed.isEmpty()) {
|
||||||
|
llMnemonic.setVisibility(View.VISIBLE);
|
||||||
tvWalletMnemonic.setText(seed);
|
tvWalletMnemonic.setText(seed);
|
||||||
|
}
|
||||||
|
boolean showAdvanced = false;
|
||||||
|
if (isKeyValid(viewKey)) {
|
||||||
|
llViewKey.setVisibility(View.VISIBLE);
|
||||||
tvWalletViewKey.setText(viewKey);
|
tvWalletViewKey.setText(viewKey);
|
||||||
|
showAdvanced = true;
|
||||||
|
}
|
||||||
|
if (isKeyValid(spendKey)) {
|
||||||
|
llSpendKey.setVisibility(View.VISIBLE);
|
||||||
tvWalletSpendKey.setText(spendKey);
|
tvWalletSpendKey.setText(spendKey);
|
||||||
bAdvancedInfo.setVisibility(View.VISIBLE);
|
showAdvanced = true;
|
||||||
|
}
|
||||||
|
if (showAdvanced) bAdvancedInfo.setVisibility(View.VISIBLE);
|
||||||
bCopyAddress.setClickable(true);
|
bCopyAddress.setClickable(true);
|
||||||
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
|
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
|
||||||
activityCallback.setTitle(name, getString(R.string.details_title));
|
activityCallback.setTitle(name, getString(R.string.details_title));
|
||||||
|
@ -267,6 +304,8 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
public interface ProgressListener {
|
public interface ProgressListener {
|
||||||
void showProgressDialog(int msgId);
|
void showProgressDialog(int msgId);
|
||||||
|
|
||||||
|
void showLedgerProgressDialog(int mode);
|
||||||
|
|
||||||
void dismissProgressDialog();
|
void dismissProgressDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,4 +616,10 @@ public class GenerateReviewFragment extends Fragment {
|
||||||
return openDialog;
|
return openDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isKeyValid(String key) {
|
||||||
|
return (key != null) && (key.length() == 64)
|
||||||
|
&& !key.equals("0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
|
&& !key.toLowerCase().equals("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||||
|
// ledger implmenetation returns the spend key as f's
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,20 @@
|
||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.hardware.usb.UsbManager;
|
||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
|
@ -46,6 +48,8 @@ import com.m2049r.xmrwallet.dialog.AboutFragment;
|
||||||
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||||
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
|
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
|
||||||
|
import com.m2049r.xmrwallet.ledger.Ledger;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
@ -72,10 +76,10 @@ import java.util.Locale;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class LoginActivity extends SecureActivity
|
public class LoginActivity extends BaseActivity
|
||||||
implements LoginFragment.Listener, GenerateFragment.Listener,
|
implements LoginFragment.Listener, GenerateFragment.Listener,
|
||||||
GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener,
|
GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener,
|
||||||
GenerateReviewFragment.ProgressListener, ReceiveFragment.Listener {
|
ReceiveFragment.Listener {
|
||||||
private static final String GENERATE_STACK = "gen";
|
private static final String GENERATE_STACK = "gen";
|
||||||
|
|
||||||
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
|
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
|
||||||
|
@ -102,6 +106,11 @@ public class LoginActivity extends SecureActivity
|
||||||
toolbar.setTitle(title, subtitle);
|
toolbar.setTitle(title, subtitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLedger() {
|
||||||
|
return Ledger.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
Timber.d("onCreate()");
|
Timber.d("onCreate()");
|
||||||
|
@ -140,6 +149,8 @@ public class LoginActivity extends SecureActivity
|
||||||
} else {
|
} else {
|
||||||
Timber.i("Waiting for permissions");
|
Timber.i("Waiting for permissions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processIntent(getIntent());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean checkServiceRunning() {
|
boolean checkServiceRunning() {
|
||||||
|
@ -517,39 +528,12 @@ public class LoginActivity extends SecureActivity
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressDialog progressDialog = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showProgressDialog(int msgId) {
|
|
||||||
showProgressDialog(msgId, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showProgressDialog(int msgId, long delay) {
|
|
||||||
dismissProgressDialog(); // just in case
|
|
||||||
progressDialog = new MyProgressDialog(LoginActivity.this, msgId);
|
|
||||||
if (delay > 0) {
|
|
||||||
Handler handler = new Handler();
|
|
||||||
handler.postDelayed(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (progressDialog != null) progressDialog.show();
|
|
||||||
}
|
|
||||||
}, delay);
|
|
||||||
} else {
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dismissProgressDialog() {
|
|
||||||
if (progressDialog != null && progressDialog.isShowing()) {
|
|
||||||
progressDialog.dismiss();
|
|
||||||
}
|
|
||||||
progressDialog = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
|
Timber.d("onDestroy");
|
||||||
dismissProgressDialog();
|
dismissProgressDialog();
|
||||||
|
unregisterDetachReceiver();
|
||||||
|
Ledger.disconnect();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,25 +546,9 @@ public class LoginActivity extends SecureActivity
|
||||||
// and show a progress dialog, but only if there isn't one already
|
// and show a progress dialog, but only if there isn't one already
|
||||||
new AsyncWaitForService().execute();
|
new AsyncWaitForService().execute();
|
||||||
}
|
}
|
||||||
|
if (!Ledger.isConnected()) attachLedger();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyProgressDialog extends ProgressDialog {
|
|
||||||
Activity activity;
|
|
||||||
|
|
||||||
MyProgressDialog(Activity activity, int msgId) {
|
|
||||||
super(activity);
|
|
||||||
this.activity = activity;
|
|
||||||
setCancelable(false);
|
|
||||||
setMessage(activity.getString(msgId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
// prevent back button
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class AsyncWaitForService extends AsyncTask<Void, Void, Void> {
|
private class AsyncWaitForService extends AsyncTask<Void, Void, Void> {
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
|
@ -730,8 +698,13 @@ public class LoginActivity extends SecureActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
|
acquireWakeLock();
|
||||||
|
if (walletCreator.isLedger()) {
|
||||||
|
showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
||||||
|
} else {
|
||||||
showProgressDialog(R.string.generate_wallet_creating);
|
showProgressDialog(R.string.generate_wallet_creating);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
@ -763,6 +736,7 @@ public class LoginActivity extends SecureActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
|
releaseWakeLock(RELEASE_WAKE_LOCK_DELAY);
|
||||||
if (isDestroyed()) {
|
if (isDestroyed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -794,12 +768,20 @@ public class LoginActivity extends SecureActivity
|
||||||
interface WalletCreator {
|
interface WalletCreator {
|
||||||
boolean createWallet(File aFile, String password);
|
boolean createWallet(File aFile, String password);
|
||||||
|
|
||||||
|
boolean isLedger();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGenerate(final String name, final String password) {
|
public void onGenerate(final String name, final String password) {
|
||||||
createWallet(name, password,
|
createWallet(name, password,
|
||||||
new WalletCreator() {
|
new WalletCreator() {
|
||||||
|
@Override
|
||||||
|
public boolean isLedger() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean createWallet(File aFile, String password) {
|
public boolean createWallet(File aFile, String password) {
|
||||||
Wallet newWallet = WalletManager.getInstance()
|
Wallet newWallet = WalletManager.getInstance()
|
||||||
.createWallet(aFile, password, MNEMONIC_LANGUAGE);
|
.createWallet(aFile, password, MNEMONIC_LANGUAGE);
|
||||||
|
@ -819,9 +801,40 @@ public class LoginActivity extends SecureActivity
|
||||||
final long restoreHeight) {
|
final long restoreHeight) {
|
||||||
createWallet(name, password,
|
createWallet(name, password,
|
||||||
new WalletCreator() {
|
new WalletCreator() {
|
||||||
|
@Override
|
||||||
|
public boolean isLedger() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean createWallet(File aFile, String password) {
|
public boolean createWallet(File aFile, String password) {
|
||||||
Wallet newWallet = WalletManager.getInstance().
|
Wallet newWallet = WalletManager.getInstance()
|
||||||
recoveryWallet(aFile, password, seed, restoreHeight);
|
.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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGenerateLedger(final String name, final String password, final long restoreHeight) {
|
||||||
|
createWallet(name, password,
|
||||||
|
new WalletCreator() {
|
||||||
|
@Override
|
||||||
|
public boolean isLedger() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createWallet(File aFile, String password) {
|
||||||
|
Wallet newWallet = WalletManager.getInstance()
|
||||||
|
.createWalletFromDevice(aFile, password,
|
||||||
|
restoreHeight, "Ledger");
|
||||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Timber.e(newWallet.getErrorString());
|
Timber.e(newWallet.getErrorString());
|
||||||
|
@ -839,6 +852,12 @@ public class LoginActivity extends SecureActivity
|
||||||
final long restoreHeight) {
|
final long restoreHeight) {
|
||||||
createWallet(name, password,
|
createWallet(name, password,
|
||||||
new WalletCreator() {
|
new WalletCreator() {
|
||||||
|
@Override
|
||||||
|
public boolean isLedger() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean createWallet(File aFile, String password) {
|
public boolean createWallet(File aFile, String password) {
|
||||||
Wallet newWallet = WalletManager.getInstance()
|
Wallet newWallet = WalletManager.getInstance()
|
||||||
.createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight,
|
.createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight,
|
||||||
|
@ -863,6 +882,15 @@ public class LoginActivity extends SecureActivity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void toast(final int msgId) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(LoginActivity.this, getString(msgId), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAccept(final String name, final String password) {
|
public void onAccept(final String name, final String password) {
|
||||||
File walletFolder = getStorageRoot();
|
File walletFolder = getStorageRoot();
|
||||||
|
@ -870,26 +898,9 @@ public class LoginActivity extends SecureActivity
|
||||||
Timber.d("New Wallet %s", walletFile.getAbsolutePath());
|
Timber.d("New Wallet %s", walletFile.getAbsolutePath());
|
||||||
walletFile.delete(); // when recovering wallets, the cache seems corrupt
|
walletFile.delete(); // when recovering wallets, the cache seems corrupt
|
||||||
|
|
||||||
boolean rc = testWallet(walletFile.getAbsolutePath(), password) == Wallet.Status.Status_Ok;
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
popFragmentStack(GENERATE_STACK);
|
popFragmentStack(GENERATE_STACK);
|
||||||
Toast.makeText(LoginActivity.this,
|
Toast.makeText(LoginActivity.this,
|
||||||
getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show();
|
getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show();
|
||||||
} else {
|
|
||||||
Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath());
|
|
||||||
Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Wallet.Status testWallet(String path, String password) {
|
|
||||||
Timber.d("testing wallet %s", path);
|
|
||||||
Wallet aWallet = WalletManager.getInstance().openWallet(path, password);
|
|
||||||
if (aWallet == null) return Wallet.Status.Status_Error; // does this ever happen?
|
|
||||||
Wallet.Status status = aWallet.getStatus();
|
|
||||||
Timber.d("wallet tested %s", aWallet.getStatus());
|
|
||||||
aWallet.close();
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean walletExists(File walletFile, boolean any) {
|
boolean walletExists(File walletFile, boolean any) {
|
||||||
|
@ -1040,6 +1051,9 @@ public class LoginActivity extends SecureActivity
|
||||||
case R.id.action_create_help_seed:
|
case R.id.action_create_help_seed:
|
||||||
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed);
|
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed);
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_create_help_ledger:
|
||||||
|
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_ledger);
|
||||||
|
return true;
|
||||||
case R.id.action_details_help:
|
case R.id.action_details_help:
|
||||||
HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
|
HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1150,14 +1164,166 @@ public class LoginActivity extends SecureActivity
|
||||||
File walletFile = Helper.getWalletFile(this, walletNode.getName());
|
File walletFile = Helper.getWalletFile(this, walletNode.getName());
|
||||||
if (WalletManager.getInstance().walletExists(walletFile)) {
|
if (WalletManager.getInstance().walletExists(walletFile)) {
|
||||||
WalletManager.getInstance().setDaemon(walletNode);
|
WalletManager.getInstance().setDaemon(walletNode);
|
||||||
Helper.promptPassword(LoginActivity.this, walletNode.getName(), false, new Helper.PasswordAction() {
|
Helper.promptPassword(LoginActivity.this, walletNode.getName(), false,
|
||||||
|
new Helper.PasswordAction() {
|
||||||
@Override
|
@Override
|
||||||
public void action(String walletName, String password, boolean fingerprintUsed) {
|
public void action(String walletName, String password, boolean fingerprintUsed) {
|
||||||
|
String keyPath = new File(Helper.getWalletRoot(LoginActivity.this),
|
||||||
|
walletName + ".keys").getAbsolutePath();
|
||||||
|
// check if we need connected hardware
|
||||||
|
int hw = WalletManager.getInstance().queryWalletHardware(keyPath, password);
|
||||||
|
if ((hw == 1) && (!hasLedger())) {
|
||||||
|
toast(R.string.open_wallet_ledger_missing);
|
||||||
|
} else {
|
||||||
|
// hw could be < 0 meaning the password is wrong - this gets dealt with later
|
||||||
startWallet(walletName, password, fingerprintUsed);
|
startWallet(walletName, password, fingerprintUsed);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else { // this cannot really happen as we prefilter choices
|
} else { // this cannot really happen as we prefilter choices
|
||||||
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// USB Stuff - (Ledger)
|
||||||
|
|
||||||
|
private static final String ACTION_USB_PERMISSION = "com.m2049r.xmrwallet.USB_PERMISSION";
|
||||||
|
|
||||||
|
void attachLedger() {
|
||||||
|
final UsbManager usbManager = getUsbManager();
|
||||||
|
UsbDevice device = Ledger.findDevice(usbManager);
|
||||||
|
if (device != null) {
|
||||||
|
if (usbManager.hasPermission(device)) {
|
||||||
|
connectLedger(usbManager, device);
|
||||||
|
} else {
|
||||||
|
registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
|
||||||
|
usbManager.requestPermission(device,
|
||||||
|
PendingIntent.getBroadcast(this, 0,
|
||||||
|
new Intent(ACTION_USB_PERMISSION), 0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.d("no ledger device found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver usbPermissionReceiver = new BroadcastReceiver() {
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (ACTION_USB_PERMISSION.equals(action)) {
|
||||||
|
unregisterReceiver(usbPermissionReceiver);
|
||||||
|
synchronized (this) {
|
||||||
|
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||||
|
if (device != null) {
|
||||||
|
connectLedger(getUsbManager(), device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.w("User denied permission for device %s", device.getProductName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void connectLedger(UsbManager usbManager, final UsbDevice usbDevice) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException ex) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(LoginActivity.this,
|
||||||
|
getString(R.string.open_wallet_ledger_missing),
|
||||||
|
Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
processIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processIntent(Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
|
||||||
|
synchronized (this) {
|
||||||
|
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
if (device != null) {
|
||||||
|
final UsbManager usbManager = getUsbManager();
|
||||||
|
if (usbManager.hasPermission(device)) {
|
||||||
|
Timber.d("Ledger attached by intent");
|
||||||
|
connectLedger(usbManager, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BroadcastReceiver detachReceiver;
|
||||||
|
|
||||||
|
private void unregisterDetachReceiver() {
|
||||||
|
if (detachReceiver != null) unregisterReceiver(detachReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerDetachReceiver() {
|
||||||
|
detachReceiver = new BroadcastReceiver() {
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||||
|
unregisterDetachReceiver();
|
||||||
|
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
Timber.i("Ledger detached!");
|
||||||
|
if (device != null)
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(LoginActivity.this,
|
||||||
|
getString(R.string.toast_ledger_detached, device.getProductName()),
|
||||||
|
Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ledger.disconnect();
|
||||||
|
onLedgerAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerReceiver(detachReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLedgerAction() {
|
||||||
|
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
if (f instanceof GenerateFragment) {
|
||||||
|
onBackPressed();
|
||||||
|
} else if (f instanceof LoginFragment) {
|
||||||
|
if (((LoginFragment) f).isFabOpen()) {
|
||||||
|
((LoginFragment) f).animateFAB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get UsbManager or die trying
|
||||||
|
@NonNull
|
||||||
|
private UsbManager getUsbManager() {
|
||||||
|
final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||||
|
if (usbManager == null) {
|
||||||
|
throw new IllegalStateException("no USB_SERVICE");
|
||||||
|
}
|
||||||
|
return usbManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
|
|
||||||
void setNetworkType(NetworkType networkType);
|
void setNetworkType(NetworkType networkType);
|
||||||
|
|
||||||
|
boolean hasLedger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -145,11 +146,13 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
fabView = (FloatingActionButton) view.findViewById(R.id.fabView);
|
fabView = (FloatingActionButton) view.findViewById(R.id.fabView);
|
||||||
fabKey = (FloatingActionButton) view.findViewById(R.id.fabKey);
|
fabKey = (FloatingActionButton) view.findViewById(R.id.fabKey);
|
||||||
fabSeed = (FloatingActionButton) view.findViewById(R.id.fabSeed);
|
fabSeed = (FloatingActionButton) view.findViewById(R.id.fabSeed);
|
||||||
|
fabLedger = (FloatingActionButton) view.findViewById(R.id.fabLedger);
|
||||||
|
|
||||||
fabNewL = (RelativeLayout) view.findViewById(R.id.fabNewL);
|
fabNewL = (RelativeLayout) view.findViewById(R.id.fabNewL);
|
||||||
fabViewL = (RelativeLayout) view.findViewById(R.id.fabViewL);
|
fabViewL = (RelativeLayout) view.findViewById(R.id.fabViewL);
|
||||||
fabKeyL = (RelativeLayout) view.findViewById(R.id.fabKeyL);
|
fabKeyL = (RelativeLayout) view.findViewById(R.id.fabKeyL);
|
||||||
fabSeedL = (RelativeLayout) view.findViewById(R.id.fabSeedL);
|
fabSeedL = (RelativeLayout) view.findViewById(R.id.fabSeedL);
|
||||||
|
fabLedgerL = (RelativeLayout) view.findViewById(R.id.fabLedgerL);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -163,6 +166,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
fabView.setOnClickListener(this);
|
fabView.setOnClickListener(this);
|
||||||
fabKey.setOnClickListener(this);
|
fabKey.setOnClickListener(this);
|
||||||
fabSeed.setOnClickListener(this);
|
fabSeed.setOnClickListener(this);
|
||||||
|
fabLedger.setOnClickListener(this);
|
||||||
fabScreen.setOnClickListener(this);
|
fabScreen.setOnClickListener(this);
|
||||||
|
|
||||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
||||||
|
@ -426,9 +430,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFabOpen = false;
|
private boolean isFabOpen = false;
|
||||||
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed;
|
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger;
|
||||||
private FrameLayout fabScreen;
|
private FrameLayout fabScreen;
|
||||||
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL;
|
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabLedgerL;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -437,11 +441,14 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
}
|
}
|
||||||
|
|
||||||
public void animateFAB() {
|
public void animateFAB() {
|
||||||
if (isFabOpen) {
|
if (isFabOpen) { // close the fab
|
||||||
fabScreen.setVisibility(View.INVISIBLE);
|
|
||||||
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) {
|
||||||
|
fabLedgerL.startAnimation(fab_close);
|
||||||
|
fabLedger.setClickable(false);
|
||||||
|
} else {
|
||||||
fabNewL.startAnimation(fab_close);
|
fabNewL.startAnimation(fab_close);
|
||||||
fabNew.setClickable(false);
|
fabNew.setClickable(false);
|
||||||
fabViewL.startAnimation(fab_close);
|
fabViewL.startAnimation(fab_close);
|
||||||
|
@ -450,11 +457,28 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
fabKey.setClickable(false);
|
fabKey.setClickable(false);
|
||||||
fabSeedL.startAnimation(fab_close);
|
fabSeedL.startAnimation(fab_close);
|
||||||
fabSeed.setClickable(false);
|
fabSeed.setClickable(false);
|
||||||
|
}
|
||||||
isFabOpen = false;
|
isFabOpen = false;
|
||||||
} else {
|
} 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()) {
|
||||||
|
fabLedgerL.setVisibility(View.VISIBLE);
|
||||||
|
fabNewL.setVisibility(View.GONE);
|
||||||
|
fabViewL.setVisibility(View.GONE);
|
||||||
|
fabKeyL.setVisibility(View.GONE);
|
||||||
|
fabSeedL.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
fabLedgerL.startAnimation(fab_open);
|
||||||
|
fabLedger.setClickable(true);
|
||||||
|
} else {
|
||||||
|
fabLedgerL.setVisibility(View.GONE);
|
||||||
|
fabNewL.setVisibility(View.VISIBLE);
|
||||||
|
fabViewL.setVisibility(View.VISIBLE);
|
||||||
|
fabKeyL.setVisibility(View.VISIBLE);
|
||||||
|
fabSeedL.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
fabNewL.startAnimation(fab_open);
|
fabNewL.startAnimation(fab_open);
|
||||||
fabNew.setClickable(true);
|
fabNew.setClickable(true);
|
||||||
fabViewL.startAnimation(fab_open);
|
fabViewL.startAnimation(fab_open);
|
||||||
|
@ -463,6 +487,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
fabKey.setClickable(true);
|
fabKey.setClickable(true);
|
||||||
fabSeedL.startAnimation(fab_open);
|
fabSeedL.startAnimation(fab_open);
|
||||||
fabSeed.setClickable(true);
|
fabSeed.setClickable(true);
|
||||||
|
}
|
||||||
isFabOpen = true;
|
isFabOpen = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,6 +495,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int id = v.getId();
|
int id = v.getId();
|
||||||
|
Timber.d("onClick %d/%d", id, R.id.fabLedger);
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case R.id.fab:
|
case R.id.fab:
|
||||||
animateFAB();
|
animateFAB();
|
||||||
|
@ -491,6 +517,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||||
animateFAB();
|
animateFAB();
|
||||||
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
|
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
|
||||||
break;
|
break;
|
||||||
|
case R.id.fabLedger:
|
||||||
|
Timber.d("FAB_LEDGER");
|
||||||
|
animateFAB();
|
||||||
|
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
|
||||||
|
break;
|
||||||
case R.id.fabScreen:
|
case R.id.fabScreen:
|
||||||
animateFAB();
|
animateFAB();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -47,6 +47,7 @@ import com.google.zxing.common.BitMatrix;
|
||||||
import com.google.zxing.qrcode.QRCodeWriter;
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
@ -62,7 +63,6 @@ import timber.log.Timber;
|
||||||
public class ReceiveFragment extends Fragment {
|
public class ReceiveFragment extends Fragment {
|
||||||
|
|
||||||
private ProgressBar pbProgress;
|
private ProgressBar pbProgress;
|
||||||
private View llAddress;
|
|
||||||
private TextView tvAddressLabel;
|
private TextView tvAddressLabel;
|
||||||
private TextView tvAddress;
|
private TextView tvAddress;
|
||||||
private TextInputLayout etPaymentId;
|
private TextInputLayout etPaymentId;
|
||||||
|
@ -93,7 +93,6 @@ public class ReceiveFragment extends Fragment {
|
||||||
View view = inflater.inflate(R.layout.fragment_receive, container, false);
|
View view = inflater.inflate(R.layout.fragment_receive, container, false);
|
||||||
|
|
||||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||||
llAddress = view.findViewById(R.id.llAddress);
|
|
||||||
tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel);
|
tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel);
|
||||||
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
|
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
|
||||||
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
|
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
|
||||||
|
@ -177,23 +176,9 @@ public class ReceiveFragment extends Fragment {
|
||||||
enableSubaddressButton(false);
|
enableSubaddressButton(false);
|
||||||
enableCopyAddress(false);
|
enableCopyAddress(false);
|
||||||
|
|
||||||
final Runnable resetSize = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
tvAddress.animate().setDuration(125).scaleX(1).scaleY(1).start();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final Runnable newAddress = new Runnable() {
|
final Runnable newAddress = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
tvAddress.setText(wallet.getNewSubaddress());
|
getNewSubaddress();
|
||||||
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
|
|
||||||
wallet.getNumSubaddresses() - 1));
|
|
||||||
storeWallet();
|
|
||||||
generateQr();
|
|
||||||
enableCopyAddress(true);
|
|
||||||
tvAddress.animate().alpha(1).setDuration(125)
|
|
||||||
.scaleX(1.2f).scaleY(1.2f)
|
|
||||||
.withEndAction(resetSize).start();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -315,18 +300,39 @@ public class ReceiveFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadAndShow(String walletPath, String password) {
|
private void loadAndShow(String walletPath, String password) {
|
||||||
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR,
|
new AsyncShow(walletPath, password).executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||||
walletPath, password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
|
GenerateReviewFragment.ProgressListener progressCallback = null;
|
||||||
String password;
|
|
||||||
|
private class AsyncShow extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
final private String walletPath;
|
||||||
|
final private String password;
|
||||||
|
|
||||||
|
|
||||||
|
AsyncShow(String walletPath, String passsword) {
|
||||||
|
super();
|
||||||
|
this.walletPath = walletPath;
|
||||||
|
this.password = passsword;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean dialogOpened = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(String... params) {
|
protected void onPreExecute() {
|
||||||
if (params.length != 2) return false;
|
super.onPreExecute();
|
||||||
String walletPath = params[0];
|
showProgress();
|
||||||
password = params[1];
|
if ((walletPath != null)
|
||||||
|
&& (WalletManager.getInstance().queryWalletHardware(walletPath + ".keys", password) == 1)
|
||||||
|
&& (progressCallback != null)) {
|
||||||
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
||||||
|
dialogOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
if (params.length != 0) return false;
|
||||||
wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
||||||
isMyWallet = true;
|
isMyWallet = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -335,6 +341,8 @@ public class ReceiveFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
|
if (dialogOpened)
|
||||||
|
progressCallback.dismissProgressDialog();
|
||||||
if (!isAdded()) return; // never mind
|
if (!isAdded()) return; // never mind
|
||||||
if (result) {
|
if (result) {
|
||||||
show();
|
show();
|
||||||
|
@ -495,6 +503,10 @@ public class ReceiveFragment extends Fragment {
|
||||||
throw new ClassCastException(context.toString()
|
throw new ClassCastException(context.toString()
|
||||||
+ " must implement Listener");
|
+ " must implement Listener");
|
||||||
}
|
}
|
||||||
|
if (context instanceof GenerateReviewFragment.ProgressListener) {
|
||||||
|
this.progressCallback = (GenerateReviewFragment.ProgressListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -513,4 +525,51 @@ public class ReceiveFragment extends Fragment {
|
||||||
}
|
}
|
||||||
super.onDetach();
|
super.onDetach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getNewSubaddress() {
|
||||||
|
new AsyncSubaddress().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AsyncSubaddress extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
private String newSubaddress;
|
||||||
|
|
||||||
|
boolean dialogOpened = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
if (wallet.isKeyOnDevice() && (progressCallback != null)) {
|
||||||
|
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
|
||||||
|
dialogOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
if (params.length != 0) return false;
|
||||||
|
newSubaddress = wallet.getNewSubaddress();
|
||||||
|
storeWallet();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
if (dialogOpened)
|
||||||
|
progressCallback.dismissProgressDialog();
|
||||||
|
tvAddress.setText(newSubaddress);
|
||||||
|
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
|
||||||
|
wallet.getNumSubaddresses() - 1));
|
||||||
|
generateQr();
|
||||||
|
enableCopyAddress(true);
|
||||||
|
final Runnable resetSize = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
tvAddress.animate().setDuration(125).scaleX(1).scaleY(1).start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tvAddress.animate().alpha(1).setDuration(125)
|
||||||
|
.scaleX(1.2f).scaleY(1.2f)
|
||||||
|
.withEndAction(resetSize).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
@ -51,12 +52,14 @@ import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||||
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
|
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
|
||||||
import com.m2049r.xmrwallet.fragment.send.SendFragment;
|
import com.m2049r.xmrwallet.fragment.send.SendFragment;
|
||||||
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.service.WalletService;
|
import com.m2049r.xmrwallet.service.WalletService;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||||
import com.m2049r.xmrwallet.util.UserNotes;
|
import com.m2049r.xmrwallet.util.UserNotes;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
|
@ -65,7 +68,7 @@ import java.util.List;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class WalletActivity extends SecureActivity implements WalletFragment.Listener,
|
public class WalletActivity extends BaseActivity implements WalletFragment.Listener,
|
||||||
WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
|
WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
|
||||||
GenerateReviewFragment.ListenerWithWallet,
|
GenerateReviewFragment.ListenerWithWallet,
|
||||||
GenerateReviewFragment.Listener,
|
GenerateReviewFragment.Listener,
|
||||||
|
@ -397,28 +400,6 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
Timber.d("onResume()");
|
Timber.d("onResume()");
|
||||||
}
|
}
|
||||||
|
|
||||||
private PowerManager.WakeLock wl = null;
|
|
||||||
|
|
||||||
void acquireWakeLock() {
|
|
||||||
if ((wl != null) && wl.isHeld()) return;
|
|
||||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
||||||
this.wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, getString(R.string.app_name));
|
|
||||||
try {
|
|
||||||
wl.acquire();
|
|
||||||
Timber.d("WakeLock acquired");
|
|
||||||
} catch (SecurityException ex) {
|
|
||||||
Timber.w("WakeLock NOT acquired: %s", ex.getLocalizedMessage());
|
|
||||||
wl = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void releaseWakeLock() {
|
|
||||||
if ((wl == null) || !wl.isHeld()) return;
|
|
||||||
wl.release();
|
|
||||||
wl = null;
|
|
||||||
Timber.d("WakeLock released");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveWallet() {
|
public void saveWallet() {
|
||||||
if (mIsBound) { // no point in talking to unbound service
|
if (mIsBound) { // no point in talking to unbound service
|
||||||
Intent intent = new Intent(getApplicationContext(), WalletService.class);
|
Intent intent = new Intent(getApplicationContext(), WalletService.class);
|
||||||
|
@ -503,7 +484,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
|
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
|
||||||
if (wallet.isSynchronized()) {
|
if (wallet.isSynchronized()) {
|
||||||
Timber.d("onRefreshed() synced");
|
Timber.d("onRefreshed() synced");
|
||||||
releaseWakeLock(); // the idea is to stay awake until synced
|
releaseWakeLock(RELEASE_WAKE_LOCK_DELAY); // the idea is to stay awake until synced
|
||||||
if (!synced) { // first sync
|
if (!synced) { // first sync
|
||||||
onProgress(-1);
|
onProgress(-1);
|
||||||
saveWallet(); // save on first sync
|
saveWallet(); // save on first sync
|
||||||
|
@ -544,10 +525,21 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
|
|
||||||
boolean haveWallet = false;
|
boolean haveWallet = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWalletOpen(final int hardware) {
|
||||||
|
if (hardware > 0)
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWalletStarted(final boolean success) {
|
public void onWalletStarted(final boolean success) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
dismissProgressDialog();
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
@ -578,6 +570,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
dismissProgressDialog();
|
||||||
PendingTransaction.Status status = pendingTransaction.getStatus();
|
PendingTransaction.Status status = pendingTransaction.getStatus();
|
||||||
if (status != PendingTransaction.Status.Status_Ok) {
|
if (status != PendingTransaction.Status.Status_Ok) {
|
||||||
String errorText = pendingTransaction.getErrorString();
|
String errorText = pendingTransaction.getErrorString();
|
||||||
|
@ -733,6 +726,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
intent.putExtra(WalletService.REQUEST_CMD_TX_TAG, tag);
|
intent.putExtra(WalletService.REQUEST_CMD_TX_TAG, tag);
|
||||||
startService(intent);
|
startService(intent);
|
||||||
Timber.d("CREATE TX request sent");
|
Timber.d("CREATE TX request sent");
|
||||||
|
if (getWallet().isKeyOnDevice())
|
||||||
|
showLedgerProgressDialog(LedgerProgressDialog.TYPE_SEND);
|
||||||
} else {
|
} else {
|
||||||
Timber.e("Service not bound");
|
Timber.e("Service not bound");
|
||||||
}
|
}
|
||||||
|
@ -1049,12 +1044,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
final int id = item.getItemId();
|
final int id = item.getItemId();
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case R.id.account_new:
|
case R.id.account_new:
|
||||||
getWallet().addAccount();
|
addAccount();
|
||||||
int newIdx = getWallet().getNumAccounts() - 1;
|
|
||||||
getWallet().setAccountIndex(newIdx);
|
|
||||||
Toast.makeText(this,
|
|
||||||
getString(R.string.accounts_new, newIdx),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Timber.d("NavigationDrawer ID=%d", id);
|
Timber.d("NavigationDrawer ID=%d", id);
|
||||||
|
@ -1063,9 +1053,49 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||||
Timber.d("found @%d", accountIdx);
|
Timber.d("found @%d", accountIdx);
|
||||||
getWallet().setAccountIndex(accountIdx);
|
getWallet().setAccountIndex(accountIdx);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
drawer.closeDrawer(GravityCompat.START);
|
drawer.closeDrawer(GravityCompat.START);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addAccount() {
|
||||||
|
new AsyncAddAccount().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AsyncAddAccount extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
boolean dialogOpened = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
if (getWallet().isKeyOnDevice()) {
|
||||||
|
showLedgerProgressDialog(LedgerProgressDialog.TYPE_ACCOUNT);
|
||||||
|
dialogOpened = true;
|
||||||
|
} else {
|
||||||
|
showProgressDialog(R.string.accounts_progress_new);
|
||||||
|
dialogOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
if (params.length != 0) return false;
|
||||||
|
getWallet().addAccount();
|
||||||
|
getWallet().setAccountIndex(getWallet().getNumAccounts() - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
forceUpdate();
|
||||||
|
drawer.closeDrawer(GravityCompat.START);
|
||||||
|
if (dialogOpened)
|
||||||
|
dismissProgressDialog();
|
||||||
|
Toast.makeText(WalletActivity.this,
|
||||||
|
getString(R.string.accounts_new, getWallet().getNumAccounts() - 1),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package com.m2049r.xmrwallet.dialog;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Android Open Source Project
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class ProgressDialog extends AlertDialog {
|
||||||
|
|
||||||
|
private ProgressBar pbBar;
|
||||||
|
|
||||||
|
private TextView tvMessage;
|
||||||
|
|
||||||
|
private TextView tvProgress;
|
||||||
|
|
||||||
|
private View rlProgressBar, pbCircle;
|
||||||
|
|
||||||
|
static private final String PROGRESS_FORMAT = "%1d/%2d";
|
||||||
|
|
||||||
|
private CharSequence message;
|
||||||
|
private int maxValue, progressValue;
|
||||||
|
private boolean indeterminate = true;
|
||||||
|
|
||||||
|
public ProgressDialog(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
final View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ledger_progress, null);
|
||||||
|
pbCircle = view.findViewById(R.id.pbCircle);
|
||||||
|
tvMessage = (TextView) view.findViewById(R.id.tvMessage);
|
||||||
|
rlProgressBar = view.findViewById(R.id.rlProgressBar);
|
||||||
|
pbBar = (ProgressBar) view.findViewById(R.id.pbBar);
|
||||||
|
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
|
||||||
|
setView(view);
|
||||||
|
//setTitle("blabla");
|
||||||
|
//super.setMessage("bubbu");
|
||||||
|
// view.invalidate();
|
||||||
|
setIndeterminate(indeterminate);
|
||||||
|
if (maxValue > 0) {
|
||||||
|
setMax(maxValue);
|
||||||
|
}
|
||||||
|
if (progressValue > 0) {
|
||||||
|
setProgress(progressValue);
|
||||||
|
}
|
||||||
|
if (message != null) {
|
||||||
|
Timber.d("msg=%s", message);
|
||||||
|
setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgress(int value, int max) {
|
||||||
|
progressValue = value;
|
||||||
|
maxValue = max;
|
||||||
|
if (pbBar != null) {
|
||||||
|
pbBar.setProgress(value);
|
||||||
|
pbBar.setMax(max);
|
||||||
|
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgress(int value) {
|
||||||
|
progressValue = value;
|
||||||
|
if (pbBar != null) {
|
||||||
|
pbBar.setProgress(value);
|
||||||
|
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(int max) {
|
||||||
|
maxValue = max;
|
||||||
|
if (pbBar != null) {
|
||||||
|
pbBar.setMax(max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndeterminate(boolean indeterminate) {
|
||||||
|
if (this.indeterminate != indeterminate) {
|
||||||
|
if (rlProgressBar != null) {
|
||||||
|
if (indeterminate) {
|
||||||
|
pbCircle.setVisibility(View.VISIBLE);
|
||||||
|
rlProgressBar.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
pbCircle.setVisibility(View.GONE);
|
||||||
|
rlProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.indeterminate = indeterminate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMessage(CharSequence message) {
|
||||||
|
this.message = message;
|
||||||
|
if (tvMessage != null) {
|
||||||
|
tvMessage.setText(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* 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.ledger;
|
||||||
|
|
||||||
|
public enum Instruction {
|
||||||
|
INS_NONE(0x00),
|
||||||
|
INS_RESET(0x02),
|
||||||
|
INS_GET_KEY(0x20),
|
||||||
|
INS_PUT_KEY(0x22),
|
||||||
|
INS_GET_CHACHA8_PREKEY(0x24),
|
||||||
|
INS_VERIFY_KEY(0x26),
|
||||||
|
|
||||||
|
INS_SECRET_KEY_TO_PUBLIC_KEY(0x30),
|
||||||
|
INS_GEN_KEY_DERIVATION(0x32),
|
||||||
|
INS_DERIVATION_TO_SCALAR(0x34),
|
||||||
|
INS_DERIVE_PUBLIC_KEY(0x36),
|
||||||
|
INS_DERIVE_SECRET_KEY(0x38),
|
||||||
|
INS_GEN_KEY_IMAGE(0x3A),
|
||||||
|
INS_SECRET_KEY_ADD(0x3C),
|
||||||
|
INS_SECRET_KEY_SUB(0x3E),
|
||||||
|
INS_GENERATE_KEYPAIR(0x40),
|
||||||
|
INS_SECRET_SCAL_MUL_KEY(0x42),
|
||||||
|
INS_SECRET_SCAL_MUL_BASE(0x44),
|
||||||
|
|
||||||
|
INS_DERIVE_SUBADDRESS_PUBLIC_KEY(0x46),
|
||||||
|
INS_GET_SUBADDRESS(0x48),
|
||||||
|
INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY(0x4A),
|
||||||
|
INS_GET_SUBADDRESS_SECRET_KEY(0x4C),
|
||||||
|
|
||||||
|
INS_OPEN_TX(0x70),
|
||||||
|
INS_SET_SIGNATURE_MODE(0x72),
|
||||||
|
INS_GET_ADDITIONAL_KEY(0x74),
|
||||||
|
INS_STEALTH(0x76),
|
||||||
|
INS_BLIND(0x78),
|
||||||
|
INS_UNBLIND(0x7A),
|
||||||
|
INS_VALIDATE(0x7C),
|
||||||
|
INS_MLSAG(0x7E),
|
||||||
|
INS_CLOSE_TX(0x80),
|
||||||
|
|
||||||
|
INS_GET_RESPONSE(0xc0),
|
||||||
|
|
||||||
|
INS_UNDEFINED(0xff);;
|
||||||
|
|
||||||
|
public static Instruction fromByte(byte n) {
|
||||||
|
switch (n & 0xFF) {
|
||||||
|
case 0x00:
|
||||||
|
return INS_NONE;
|
||||||
|
case 0x02:
|
||||||
|
return INS_RESET;
|
||||||
|
|
||||||
|
case 0x20:
|
||||||
|
return INS_GET_KEY;
|
||||||
|
case 0x22:
|
||||||
|
return INS_PUT_KEY;
|
||||||
|
case 0x24:
|
||||||
|
return INS_GET_CHACHA8_PREKEY;
|
||||||
|
case 0x26:
|
||||||
|
return INS_VERIFY_KEY;
|
||||||
|
|
||||||
|
case 0x30:
|
||||||
|
return INS_SECRET_KEY_TO_PUBLIC_KEY;
|
||||||
|
case 0x32:
|
||||||
|
return INS_GEN_KEY_DERIVATION;
|
||||||
|
case 0x34:
|
||||||
|
return INS_DERIVATION_TO_SCALAR;
|
||||||
|
case 0x36:
|
||||||
|
return INS_DERIVE_PUBLIC_KEY;
|
||||||
|
case 0x38:
|
||||||
|
return INS_DERIVE_SECRET_KEY;
|
||||||
|
case 0x3A:
|
||||||
|
return INS_GEN_KEY_IMAGE;
|
||||||
|
case 0x3C:
|
||||||
|
return INS_SECRET_KEY_ADD;
|
||||||
|
case 0x3E:
|
||||||
|
return INS_SECRET_KEY_SUB;
|
||||||
|
case 0x40:
|
||||||
|
return INS_GENERATE_KEYPAIR;
|
||||||
|
case 0x42:
|
||||||
|
return INS_SECRET_SCAL_MUL_KEY;
|
||||||
|
case 0x44:
|
||||||
|
return INS_SECRET_SCAL_MUL_BASE;
|
||||||
|
|
||||||
|
case 0x46:
|
||||||
|
return INS_DERIVE_SUBADDRESS_PUBLIC_KEY;
|
||||||
|
case 0x48:
|
||||||
|
return INS_GET_SUBADDRESS;
|
||||||
|
case 0x4A:
|
||||||
|
return INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY;
|
||||||
|
case 0x4C:
|
||||||
|
return INS_GET_SUBADDRESS_SECRET_KEY;
|
||||||
|
|
||||||
|
case 0x70:
|
||||||
|
return INS_OPEN_TX;
|
||||||
|
case 0x72:
|
||||||
|
return INS_SET_SIGNATURE_MODE;
|
||||||
|
case 0x74:
|
||||||
|
return INS_GET_ADDITIONAL_KEY;
|
||||||
|
case 0x76:
|
||||||
|
return INS_STEALTH;
|
||||||
|
case 0x78:
|
||||||
|
return INS_BLIND;
|
||||||
|
case 0x7A:
|
||||||
|
return INS_UNBLIND;
|
||||||
|
case 0x7C:
|
||||||
|
return INS_VALIDATE;
|
||||||
|
case 0x7E:
|
||||||
|
return INS_MLSAG;
|
||||||
|
case 0x80:
|
||||||
|
return INS_CLOSE_TX;
|
||||||
|
|
||||||
|
case 0xc0:
|
||||||
|
return INS_GET_RESPONSE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return INS_UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
Instruction(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
|
* (c) 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.ledger;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.hardware.usb.UsbManager;
|
||||||
|
|
||||||
|
import com.btchip.BTChipException;
|
||||||
|
import com.btchip.comm.BTChipTransport;
|
||||||
|
import com.btchip.comm.android.BTChipTransportAndroidHID;
|
||||||
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class Ledger {
|
||||||
|
// lookahead parameters as suggest on
|
||||||
|
// https://monero.stackexchange.com/a/9902/8977 (Step 8)
|
||||||
|
// by dEBRUYNE
|
||||||
|
static public final int LOOKAHEAD_ACCOUNTS = 3;
|
||||||
|
static public final int LOOKAHEAD_SUBADDRESSES = 100;
|
||||||
|
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
|
||||||
|
|
||||||
|
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 UsbDevice findDevice(UsbManager usbManager) {
|
||||||
|
return BTChipTransportAndroidHID.getDevice(usbManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private Ledger Instance = null;
|
||||||
|
|
||||||
|
static public String connect(UsbManager usbManager, UsbDevice usbDevice) throws IOException {
|
||||||
|
if (Instance != null) {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
Instance = new Ledger(usbManager, usbDevice);
|
||||||
|
return Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void disconnect() {
|
||||||
|
// this is not synchronized so as to close immediately
|
||||||
|
if (Instance != null) {
|
||||||
|
Instance.close();
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public boolean isConnected() {
|
||||||
|
//TODO synchronize with connect/disconnect?
|
||||||
|
return Instance != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String Name() {
|
||||||
|
if (Instance != null) {
|
||||||
|
return Instance.name;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public byte[] Exchange(byte[] apdu) {
|
||||||
|
if (Instance != null) {
|
||||||
|
Timber.d("INS: %s", Instruction.fromByte(apdu[1]));
|
||||||
|
return Instance.exchangeRaw(apdu);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final private BTChipTransport transport;
|
||||||
|
final private String name;
|
||||||
|
private int lastSW = 0;
|
||||||
|
|
||||||
|
private Ledger(UsbManager usbManager, UsbDevice usbDevice) throws IOException {
|
||||||
|
final BTChipTransport transport = BTChipTransportAndroidHID.open(usbManager, usbDevice);
|
||||||
|
Timber.d("transport opened = %s", transport.toString());
|
||||||
|
transport.setDebug(BuildConfig.DEBUG);
|
||||||
|
this.transport = transport;
|
||||||
|
this.name = usbDevice.getManufacturerName() + " " + usbDevice.getProductName();
|
||||||
|
initKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized private void close() {
|
||||||
|
initKey(); // don't leak key after we disconnect
|
||||||
|
transport.close();
|
||||||
|
Timber.d("transport closed");
|
||||||
|
lastSW = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized private byte[] exchangeRaw(byte[] apdu) {
|
||||||
|
if (transport == null)
|
||||||
|
throw new IllegalStateException("No transport (probably closed previously)");
|
||||||
|
Timber.i("exchangeRaw %02x", apdu[1]);
|
||||||
|
Instruction ins = Instruction.fromByte(apdu[1]);
|
||||||
|
if (listener != null) listener.onInstructionSend(ins, apdu);
|
||||||
|
sniffOut(ins, apdu);
|
||||||
|
byte[] data = transport.exchange(apdu);
|
||||||
|
if (listener != null) listener.onInstructionReceive(ins, data);
|
||||||
|
sniffIn(data);
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] exchange(byte[] apdu) throws BTChipException {
|
||||||
|
byte[] response = exchangeRaw(apdu);
|
||||||
|
if (response.length < 2) {
|
||||||
|
throw new BTChipException("Truncated response");
|
||||||
|
}
|
||||||
|
lastSW = ((int) (response[response.length - 2] & 0xff) << 8) |
|
||||||
|
(int) (response[response.length - 1] & 0xff);
|
||||||
|
byte[] result = new byte[response.length - 2];
|
||||||
|
System.arraycopy(response, 0, result, 0, response.length - 2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] exchangeCheck(byte[] apdu, int acceptedSW[]) throws BTChipException {
|
||||||
|
byte[] response = exchange(apdu);
|
||||||
|
if (acceptedSW == null) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
for (int SW : acceptedSW) {
|
||||||
|
if (lastSW == SW) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
void onInstructionReceive(Instruction ins, byte[] data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener listener;
|
||||||
|
|
||||||
|
static public void setListener(Listener listener) {
|
||||||
|
if (Instance != null) {
|
||||||
|
Instance.listener = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void unsetListener(Listener listener) {
|
||||||
|
if ((Instance != null) && (Instance.listener == listener))
|
||||||
|
Instance.listener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// very stupid hack to extract the view key
|
||||||
|
// without messing around with monero core code
|
||||||
|
// NB: as all the ledger comm can be sniffed off the USB cable - there is no security issue here
|
||||||
|
private boolean snoopKey = false;
|
||||||
|
private byte[] key;
|
||||||
|
|
||||||
|
private void initKey() {
|
||||||
|
key = Helper.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String Key() {
|
||||||
|
if (Instance != null) {
|
||||||
|
return Helper.bytesToHex(Instance.key).toLowerCase();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sniffOut(Instruction ins, byte[] apdu) {
|
||||||
|
if (ins == Instruction.INS_GET_KEY) {
|
||||||
|
snoopKey = (apdu[2] == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sniffIn(byte[] data) {
|
||||||
|
// stupid hack to extract the view key
|
||||||
|
// without messing around with monero core code
|
||||||
|
if (snoopKey) {
|
||||||
|
if (data.length == 34) { // 32 key + result code 9000
|
||||||
|
long sw = ((data[data.length - 2] & 0xff) << 8) |
|
||||||
|
(data[data.length - 1] & 0xff);
|
||||||
|
Timber.e("WS %d", sw);
|
||||||
|
if (sw == SW_OK) {
|
||||||
|
System.arraycopy(data, 0, key, 0, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snoopKey = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package com.m2049r.xmrwallet.ledger;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.dialog.ProgressDialog;
|
||||||
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class LedgerProgressDialog extends ProgressDialog implements Ledger.Listener {
|
||||||
|
|
||||||
|
static public final int TYPE_DEBUG = 0;
|
||||||
|
static public final int TYPE_RESTORE = 1;
|
||||||
|
static public final int TYPE_SUBADDRESS = 2;
|
||||||
|
static public final int TYPE_ACCOUNT = 3;
|
||||||
|
static public final int TYPE_SEND = 4;
|
||||||
|
|
||||||
|
private final int type;
|
||||||
|
private Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
public LedgerProgressDialog(Context context, int type) {
|
||||||
|
super(context);
|
||||||
|
this.type = type;
|
||||||
|
setCancelable(false);
|
||||||
|
if (type == TYPE_SEND)
|
||||||
|
setMessage(context.getString(R.string.info_prepare_tx));
|
||||||
|
else
|
||||||
|
setMessage(context.getString(R.string.progress_ledger_progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
// prevent back button
|
||||||
|
}
|
||||||
|
|
||||||
|
private int firstSubaddress = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
private boolean validate = false;
|
||||||
|
private boolean validated = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInstructionSend(final Instruction ins, final byte[] apdu) {
|
||||||
|
Timber.d("LedgerProgressDialog SEND %s", ins);
|
||||||
|
uiHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (type > TYPE_DEBUG) {
|
||||||
|
validate = false;
|
||||||
|
switch (ins) {
|
||||||
|
case INS_RESET: // ledger may ask for confirmation - maybe a bug?
|
||||||
|
case INS_GET_KEY: // ledger asks for confirmation to send keys
|
||||||
|
setIndeterminate(true);
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_confirm));
|
||||||
|
break;
|
||||||
|
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
|
||||||
|
//00 4a 00 00 09 00 01000000 30000000
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 a b c d
|
||||||
|
int account = bytesToInteger(apdu, 6);
|
||||||
|
int subaddress = bytesToInteger(apdu, 10);
|
||||||
|
Timber.d("fetching subaddress (%d, %d)", account, subaddress);
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_RESTORE:
|
||||||
|
setProgress(account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress + 1,
|
||||||
|
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
|
||||||
|
setIndeterminate(false);
|
||||||
|
break;
|
||||||
|
case TYPE_ACCOUNT:
|
||||||
|
final int requestedSubaddress = account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress;
|
||||||
|
if (firstSubaddress > requestedSubaddress) {
|
||||||
|
firstSubaddress = requestedSubaddress;
|
||||||
|
}
|
||||||
|
setProgress(requestedSubaddress - firstSubaddress + 1,
|
||||||
|
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
|
||||||
|
setIndeterminate(false);
|
||||||
|
break;
|
||||||
|
case TYPE_SUBADDRESS:
|
||||||
|
if (firstSubaddress > subaddress) {
|
||||||
|
firstSubaddress = subaddress;
|
||||||
|
}
|
||||||
|
setProgress(subaddress - firstSubaddress + 1, Ledger.LOOKAHEAD_SUBADDRESSES);
|
||||||
|
setIndeterminate(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setIndeterminate(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_lookahead));
|
||||||
|
break;
|
||||||
|
case INS_VERIFY_KEY:
|
||||||
|
setIndeterminate(true);
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_verify));
|
||||||
|
break;
|
||||||
|
case INS_OPEN_TX:
|
||||||
|
setIndeterminate(true);
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_opentx));
|
||||||
|
break;
|
||||||
|
case INS_MLSAG:
|
||||||
|
if (validated) {
|
||||||
|
setIndeterminate(true);
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_mlsag));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_VALIDATE:
|
||||||
|
if ((apdu[2] != 1) || (apdu[3] != 1)) break;
|
||||||
|
validate = true;
|
||||||
|
uiHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (validate) {
|
||||||
|
setIndeterminate(true);
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_confirm));
|
||||||
|
validated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// ignore others and maintain state
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMessage(ins.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInstructionReceive(final Instruction ins, final byte[] data) {
|
||||||
|
Timber.d("LedgerProgressDialog RECV %s", ins);
|
||||||
|
uiHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (type > TYPE_DEBUG) {
|
||||||
|
switch (ins) {
|
||||||
|
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
|
||||||
|
case INS_VERIFY_KEY:
|
||||||
|
case INS_GET_CHACHA8_PREKEY:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (type != TYPE_SEND)
|
||||||
|
setMessage(getContext().getString(R.string.progress_ledger_progress));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMessage("Returned from " + ins.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we use ints in Java but the are signed; accounts & subaddresses are unsigned ...
|
||||||
|
private int bytesToInteger(byte[] bytes, int offset) {
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 3; i >= 0; i--) {
|
||||||
|
result <<= 8;
|
||||||
|
result |= (bytes[offset + i] & 0xFF);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -389,4 +389,5 @@ public class Wallet {
|
||||||
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
|
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public native boolean isKeyOnDevice();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package com.m2049r.xmrwallet.model;
|
package com.m2049r.xmrwallet.model;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.data.WalletNode;
|
import com.m2049r.xmrwallet.data.WalletNode;
|
||||||
|
import com.m2049r.xmrwallet.ledger.Ledger;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -129,6 +130,23 @@ public class WalletManager {
|
||||||
String viewKeyString,
|
String viewKeyString,
|
||||||
String spendKeyString);
|
String spendKeyString);
|
||||||
|
|
||||||
|
public Wallet createWalletFromDevice(File aFile, String password, long restoreHeight,
|
||||||
|
String deviceName) {
|
||||||
|
long walletHandle = createWalletFromDeviceJ(aFile.getAbsolutePath(), password,
|
||||||
|
getNetworkType().getValue(), deviceName, restoreHeight,
|
||||||
|
Ledger.SUBADDRESS_LOOKAHEAD);
|
||||||
|
Wallet wallet = new Wallet(walletHandle);
|
||||||
|
manageWallet(wallet);
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long createWalletFromDeviceJ(String path, String password,
|
||||||
|
int networkType,
|
||||||
|
String deviceName,
|
||||||
|
long restoreHeight,
|
||||||
|
String subaddressLookahead);
|
||||||
|
|
||||||
|
|
||||||
public native boolean closeJ(Wallet wallet);
|
public native boolean closeJ(Wallet wallet);
|
||||||
|
|
||||||
public boolean close(Wallet wallet) {
|
public boolean close(Wallet wallet) {
|
||||||
|
@ -150,6 +168,12 @@ public class WalletManager {
|
||||||
|
|
||||||
public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only);
|
public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only);
|
||||||
|
|
||||||
|
public boolean verifyWalletPasswordOnly(String keys_file_name, String password) {
|
||||||
|
return queryWalletHardware(keys_file_name, password) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public native int queryWalletHardware(String keys_file_name, String password);
|
||||||
|
|
||||||
//public native List<String> findWallets(String path); // this does not work - some error in boost
|
//public native List<String> findWallets(String path); // this does not work - some error in boost
|
||||||
|
|
||||||
public class WalletInfo implements Comparable<WalletInfo> {
|
public class WalletInfo implements Comparable<WalletInfo> {
|
||||||
|
|
|
@ -221,6 +221,8 @@ public class WalletService extends Service {
|
||||||
void onSetNotes(boolean success);
|
void onSetNotes(boolean success);
|
||||||
|
|
||||||
void onWalletStarted(boolean success);
|
void onWalletStarted(boolean success);
|
||||||
|
|
||||||
|
void onWalletOpen(int hardware);
|
||||||
}
|
}
|
||||||
|
|
||||||
String progressText = null;
|
String progressText = null;
|
||||||
|
@ -535,6 +537,8 @@ public class WalletService extends Service {
|
||||||
showProgress(30);
|
showProgress(30);
|
||||||
if (walletMgr.walletExists(path)) {
|
if (walletMgr.walletExists(path)) {
|
||||||
Timber.d("open wallet %s", path);
|
Timber.d("open wallet %s", path);
|
||||||
|
int hw = WalletManager.getInstance().queryWalletHardware(path + ".keys", walletPassword);
|
||||||
|
if (observer != null) observer.onWalletOpen(hw);
|
||||||
wallet = walletMgr.openWallet(path, walletPassword);
|
wallet = walletMgr.openWallet(path, walletPassword);
|
||||||
showProgress(60);
|
showProgress(60);
|
||||||
Timber.d("wallet opened");
|
Timber.d("wallet opened");
|
||||||
|
|
|
@ -349,33 +349,33 @@ public class Helper {
|
||||||
String walletPath = new File(getWalletRoot(context), walletName + ".keys").getAbsolutePath();
|
String walletPath = new File(getWalletRoot(context), walletName + ".keys").getAbsolutePath();
|
||||||
|
|
||||||
// try with entered password (which could be a legacy password or a CrAzYpass)
|
// try with entered password (which could be a legacy password or a CrAzYpass)
|
||||||
if (WalletManager.getInstance().verifyWalletPassword(walletPath, password, true)) {
|
if (WalletManager.getInstance().verifyWalletPasswordOnly(walletPath, password)) {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe this is a malformed CrAzYpass?
|
// maybe this is a malformed CrAzYpass?
|
||||||
String possibleCrazyPass = CrazyPassEncoder.reformat(password);
|
String possibleCrazyPass = CrazyPassEncoder.reformat(password);
|
||||||
if (possibleCrazyPass != null) { // looks like a CrAzYpass
|
if (possibleCrazyPass != null) { // looks like a CrAzYpass
|
||||||
if (WalletManager.getInstance().verifyWalletPassword(walletPath, possibleCrazyPass, true)) {
|
if (WalletManager.getInstance().verifyWalletPasswordOnly(walletPath, possibleCrazyPass)) {
|
||||||
return possibleCrazyPass;
|
return possibleCrazyPass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate & try with CrAzYpass
|
// generate & try with CrAzYpass
|
||||||
String crazyPass = KeyStoreHelper.getCrazyPass(context, password);
|
String crazyPass = KeyStoreHelper.getCrazyPass(context, password);
|
||||||
if (WalletManager.getInstance().verifyWalletPassword(walletPath, crazyPass, true)) {
|
if (WalletManager.getInstance().verifyWalletPasswordOnly(walletPath, crazyPass)) {
|
||||||
return crazyPass;
|
return crazyPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
// or maybe it is a broken CrAzYpass? (of which we have two variants)
|
// or maybe it is a broken CrAzYpass? (of which we have two variants)
|
||||||
String brokenCrazyPass2 = KeyStoreHelper.getBrokenCrazyPass(context, password, 2);
|
String brokenCrazyPass2 = KeyStoreHelper.getBrokenCrazyPass(context, password, 2);
|
||||||
if ((brokenCrazyPass2 != null)
|
if ((brokenCrazyPass2 != null)
|
||||||
&& WalletManager.getInstance().verifyWalletPassword(walletPath, brokenCrazyPass2, true)) {
|
&& WalletManager.getInstance().verifyWalletPasswordOnly(walletPath, brokenCrazyPass2)) {
|
||||||
return brokenCrazyPass2;
|
return brokenCrazyPass2;
|
||||||
}
|
}
|
||||||
String brokenCrazyPass1 = KeyStoreHelper.getBrokenCrazyPass(context, password, 1);
|
String brokenCrazyPass1 = KeyStoreHelper.getBrokenCrazyPass(context, password, 1);
|
||||||
if ((brokenCrazyPass1 != null)
|
if ((brokenCrazyPass1 != null)
|
||||||
&& WalletManager.getInstance().verifyWalletPassword(walletPath, brokenCrazyPass1, true)) {
|
&& WalletManager.getInstance().verifyWalletPasswordOnly(walletPath, brokenCrazyPass1)) {
|
||||||
return brokenCrazyPass1;
|
return brokenCrazyPass1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +407,7 @@ public class Helper {
|
||||||
final CancellationSignal cancelSignal = new CancellationSignal();
|
final CancellationSignal cancelSignal = new CancellationSignal();
|
||||||
|
|
||||||
final AtomicBoolean incorrectSavedPass = new AtomicBoolean(false);
|
final AtomicBoolean incorrectSavedPass = new AtomicBoolean(false);
|
||||||
|
|
||||||
class LoginWalletTask extends AsyncTask<Void, Void, Boolean> {
|
class LoginWalletTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
private String pass;
|
private String pass;
|
||||||
private boolean fingerprintUsed;
|
private boolean fingerprintUsed;
|
||||||
|
@ -594,6 +595,5 @@ public class Helper {
|
||||||
|
|
||||||
static public ExchangeApi getExchangeApi() {
|
static public ExchangeApi getExchangeApi() {
|
||||||
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
|
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Notice {
|
public class Notice {
|
||||||
private static final String PREFS_NAME = "notice";
|
private static final String PREFS_NAME = "notice";
|
||||||
private static List<Notice> notices = null;
|
private static List<Notice> notices = null;
|
||||||
|
@ -40,6 +42,7 @@ public class Notice {
|
||||||
private static final String NOTICE_SHOW_XMRTO_ENABLED_LOGIN = "notice_xmrto_enabled_login";
|
private static final String NOTICE_SHOW_XMRTO_ENABLED_LOGIN = "notice_xmrto_enabled_login";
|
||||||
private static final String NOTICE_SHOW_XMRTO_ENABLED_SEND = "notice_xmrto_enabled_send";
|
private static final String NOTICE_SHOW_XMRTO_ENABLED_SEND = "notice_xmrto_enabled_send";
|
||||||
private static final String NOTICE_SHOW_CRAZYPASS = "notice_crazypass_enabled_login";
|
private static final String NOTICE_SHOW_CRAZYPASS = "notice_crazypass_enabled_login";
|
||||||
|
private static final String NOTICE_SHOW_LEDGER = "notice_ledger_enabled_login";
|
||||||
|
|
||||||
private static void init() {
|
private static void init() {
|
||||||
synchronized (Notice.class) {
|
synchronized (Notice.class) {
|
||||||
|
@ -63,6 +66,12 @@ public class Notice {
|
||||||
R.string.help_details,
|
R.string.help_details,
|
||||||
2)
|
2)
|
||||||
);
|
);
|
||||||
|
notices.add(
|
||||||
|
new Notice(NOTICE_SHOW_LEDGER,
|
||||||
|
R.string.info_ledger_enabled,
|
||||||
|
R.string.help_create_ledger,
|
||||||
|
1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:fillAfter="true">
|
android:fillAfter="false">
|
||||||
<scale
|
<scale
|
||||||
android:duration="300"
|
android:duration="300"
|
||||||
android:fromXScale="1.0"
|
android:fromXScale="1.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:fillAfter="true">
|
android:fillAfter="false">
|
||||||
<scale
|
<scale
|
||||||
android:duration="300"
|
android:duration="300"
|
||||||
android:fromXScale="0.0"
|
android:fromXScale="0.0"
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="8.36dp"
|
||||||
|
android:viewportHeight="184.59"
|
||||||
|
android:viewportWidth="529.82">
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M517.82,0h-48a99.43,99.43 0,0 1,60 63.84L529.82,12A12,12 0,0 0,517.82 0Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M471.85,184.59h46a12,12 0,0 0,12 -12v-51.07A99.44,99.44 0,0 1,471.85 184.59Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M438.12,0.04v0L12,0.04a12,12 0,0 0,-12 12v160.59a12,12 0,0 0,12 12h435.25a92.69,92.69 0,0 0,-9.13 -184.54ZM435.12,142.33a49.64,49.64 0,1 1,49.64 -49.64A49.64,49.64 0,0 1,435.12 142.33Z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:paddingTop="4dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pbCircle"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMessage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
tools:text="A progress text" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/rlProgressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pbBar"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="1dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProgress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignEnd="@+id/pbBar"
|
||||||
|
android:layout_below="@id/pbBar"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
tools:text="123/600" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
|
@ -51,10 +51,13 @@
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
tools:text="49RBjxQ2zgf7t17w7So9ngcEY9obKzsrr6Dsah24MNSMiMBEeiYPP5CCTBq4GpZcEYN5Zf3upsLiwd5PezePE1i4Tf3rryY" />
|
tools:text="49RBjxQ2zgf7t17w7So9ngcEY9obKzsrr6Dsah24MNSMiMBEeiYPP5CCTBq4GpZcEYN5Zf3upsLiwd5PezePE1i4Tf3rryY" />
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/llMnemonic"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/header_top">
|
android:layout_marginTop="@dimen/header_top"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/MoneroLabel.Heading"
|
style="@style/MoneroLabel.Heading"
|
||||||
|
@ -62,7 +65,6 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:text="@string/generate_mnemonic_label" />
|
android:text="@string/generate_mnemonic_label" />
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvWalletMnemonic"
|
android:id="@+id/tvWalletMnemonic"
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
android:background="@drawable/backgound_seed"
|
android:background="@drawable/backgound_seed"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
tools:text="tucks slackens vehicle doctor oaks aloof balding knife rays wise haggled cuisine navy ladder suitcase dusted last thorn pixels karate ticket nibs violin zapped slackens" />
|
tools:text="tucks slackens vehicle doctor oaks aloof balding knife rays wise haggled cuisine navy ladder suitcase dusted last thorn pixels karate ticket nibs violin zapped slackens" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/llPassword"
|
android:id="@+id/llPassword"
|
||||||
|
@ -126,7 +129,15 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:layout_marginTop="@dimen/section_top"
|
android:layout_marginTop="@dimen/data_top"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llViewKey"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/header_top"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
|
@ -159,13 +170,21 @@
|
||||||
android:layout_marginTop="@dimen/data_top"
|
android:layout_marginTop="@dimen/data_top"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
tools:text="e4aba454d78799dbd8d576bf70e7f15a06e91f1ecfd404053f91519a48df2a0e" />
|
tools:text="e4aba454d78799dbd8d576bf70e7f15a06e91f1ecfd404053f91519a48df2a0e" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llSpendKey"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/header_top"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/MoneroLabel.Heading"
|
style="@style/MoneroLabel.Heading"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginTop="@dimen/header_top"
|
|
||||||
android:text="@string/generate_spendkey_label" />
|
android:text="@string/generate_spendkey_label" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -175,8 +194,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/data_top"
|
android:layout_marginTop="@dimen/data_top"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
tools:text="4925a6254d2dff71df53f13b8334f9ceda6cdd5821aa895f7de1d2640500740d" />
|
tools:text="2bb6afd03f1a4b91e619236b9d342aeb6d232fa50e5b9b91a00db26d1cafaa08" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
|
@ -139,6 +139,39 @@
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/fabLedgerL"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_marginBottom="88dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fabLedgerT"
|
||||||
|
style="@style/MoneroFab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/fab_restore_ledger" />
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fabLedger"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:layout_toEndOf="@+id/fabLedgerT"
|
||||||
|
android:backgroundTint="@color/gradientPink"
|
||||||
|
android:elevation="6dp"
|
||||||
|
android:src="@drawable/ic_ledger_restore"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:fabSize="mini"
|
||||||
|
app:pressedTranslationZ="12dp" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_create_help_ledger"
|
||||||
|
android:icon="@drawable/ic_help_white_24dp"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
android:title="@string/menu_help"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
</menu>
|
|
@ -204,7 +204,7 @@
|
||||||
<string name="help_xmrto"><![CDATA[
|
<string name="help_xmrto"><![CDATA[
|
||||||
<h1>BTC senden</h1>
|
<h1>BTC senden</h1>
|
||||||
<h2>XMR.TO</h2>
|
<h2>XMR.TO</h2>
|
||||||
<p>XXMR.TO ist ein Drittanbieter-Service, der als Wechselservice von Monero zu Bitcoin fungiert.
|
<p>XMR.TO ist ein Drittanbieter-Service, der als Wechselservice von Monero zu Bitcoin fungiert.
|
||||||
Wir verwenden die XMR.TO Schnittstelle, um Bitcoin-Zahlungen in Monerujo zu integrieren. Bitte sieh dir
|
Wir verwenden die XMR.TO Schnittstelle, um Bitcoin-Zahlungen in Monerujo zu integrieren. Bitte sieh dir
|
||||||
https://xmr.to an und entscheide selbst, ob es etwas ist, was du verwenden möchtest. Das Monerujo
|
https://xmr.to an und entscheide selbst, ob es etwas ist, was du verwenden möchtest. Das Monerujo
|
||||||
Team ist nicht mit XMR.TO verbunden und kann dir bei deren Service nicht helfen.</p>
|
Team ist nicht mit XMR.TO verbunden und kann dir bei deren Service nicht helfen.</p>
|
||||||
|
@ -227,4 +227,15 @@
|
||||||
indem du zum vorherigen Schritt zurückgehst und dann zum Bildschirm "Bestätigen" zurückkehrst.</p>
|
indem du zum vorherigen Schritt zurückgehst und dann zum Bildschirm "Bestätigen" zurückkehrst.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC Zahlung aktiviert - Tippe für mehr Infos.</string>
|
<string name="info_xmrto_enabled">BTC Zahlung aktiviert - Tippe für mehr Infos.</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass aktiviert - Tippe für mehr Infos.</string>
|
<string name="info_crazypass_enabled">CrAzYpass aktiviert - Tippe für mehr Infos.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger aktiviert - Tippe für mehr Infos.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Du hast eine BTC Adresse eingegeben.</b><br/>
|
<b>Du hast eine BTC Adresse eingegeben.</b><br/>
|
||||||
|
@ -321,4 +322,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Sprache</string>
|
<string name="menu_language">Sprache</string>
|
||||||
<string name="language_system_default">Benutze Systemsprache</string>
|
<string name="language_system_default">Benutze Systemsprache</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -213,4 +213,15 @@
|
||||||
πίσω στο προηγούμενο βήμα και μετά επιστρέφοντας στην οθόνη \"Επιβεβαίωση\".</p>
|
πίσω στο προηγούμενο βήμα και μετά επιστρέφοντας στην οθόνη \"Επιβεβαίωση\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
<string name="info_send_prio_fees">Υψηλότερη προτεραιότητα = Υψηλότερα Κόμιστρα</string>
|
<string name="info_send_prio_fees">Υψηλότερη προτεραιότητα = Υψηλότερα Κόμιστρα</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Συναλλαγή BTC ενεργοποιήθηκε, πάτα για περισσότερες πληροφορείες.</string>
|
<string name="info_xmrto_enabled">Συναλλαγή BTC ενεργοποιήθηκε, πάτα για περισσότερες πληροφορείες.</string>
|
||||||
|
<string name="info_crazypass_enabled">CrAzYpass ενεργοποιήθηκε, πάτα για περισσότερες πληροφορείες.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger ενεργοποιήθηκε, πάτα για περισσότερες πληροφορείες.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Έβαλες μια διεύθυνση bitcoin.</b><br/>
|
<b>Έβαλες μια διεύθυνση bitcoin.</b><br/>
|
||||||
|
@ -284,7 +286,6 @@
|
||||||
<string name="fab_restore_seed">Επαναφορά πορτοφολιού από σπόρο 25-λέξεων</string>
|
<string name="fab_restore_seed">Επαναφορά πορτοφολιού από σπόρο 25-λέξεων</string>
|
||||||
|
|
||||||
<string name="menu_changepw">Change Passphrase</string>
|
<string name="menu_changepw">Change Passphrase</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass enabled, tap for more info.</string>
|
|
||||||
<string name="changepw_progress">Change Password in progress</string>
|
<string name="changepw_progress">Change Password in progress</string>
|
||||||
<string name="changepw_failed">Change Password failed!</string>
|
<string name="changepw_failed">Change Password failed!</string>
|
||||||
<string name="changepw_success">Password changed</string>
|
<string name="changepw_success">Password changed</string>
|
||||||
|
@ -320,4 +321,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -251,4 +251,16 @@
|
||||||
parte de XMR.TO, esto se logra dando un paso atrás y luego volviendo a la pantalla de
|
parte de XMR.TO, esto se logra dando un paso atrás y luego volviendo a la pantalla de
|
||||||
\"Confirmar\".</p>
|
\"Confirmar\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -292,8 +292,10 @@
|
||||||
<string name="tx_amount_btc">Monto\n(BTC)</string>
|
<string name="tx_amount_btc">Monto\n(BTC)</string>
|
||||||
<string name="about_whoami">Soy monerujo</string>
|
<string name="about_whoami">Soy monerujo</string>
|
||||||
<string name="info_send_xmrto_success_order_label">Orden XMR.TO</string>
|
<string name="info_send_xmrto_success_order_label">Orden XMR.TO</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
|
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass activado, toca para más info.</string>
|
<string name="info_crazypass_enabled">CrAzYpass activado, toca para más info.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger activado, toca para más info.</string>
|
||||||
|
|
||||||
<string name="accounts_drawer_new">Crear Cuenta</string>
|
<string name="accounts_drawer_new">Crear Cuenta</string>
|
||||||
<string name="accounts_drawer_title">Cuentas</string>
|
<string name="accounts_drawer_title">Cuentas</string>
|
||||||
|
@ -306,4 +308,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -244,4 +244,15 @@
|
||||||
XMR.TO en retournant à l’étape précédente puis en revenant à l’écran \"Confirmation\".</p>
|
XMR.TO en retournant à l’étape précédente puis en revenant à l’écran \"Confirmation\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
<string name="info_send_prio_fees">Plus Prioritaire = Plus de Frais</string>
|
<string name="info_send_prio_fees">Plus Prioritaire = Plus de Frais</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Paiement BTC activé, tapez pour plus d\'infos.</string>
|
<string name="info_xmrto_enabled">Paiement BTC activé, tapez pour plus d\'infos.</string>
|
||||||
|
|
||||||
<string name="info_crazypass_enabled">CrAzYpass activé, tapez pour plus d\'infos.</string>
|
<string name="info_crazypass_enabled">CrAzYpass activé, tapez pour plus d\'infos.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger activé, tapez pour plus d\'infos.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Vous avez entré une adresse Bitcoin.</b><br/>
|
<b>Vous avez entré une adresse Bitcoin.</b><br/>
|
||||||
|
@ -324,4 +324,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -234,4 +234,15 @@
|
||||||
visszamész az előző lépésre, majd visszajössz a Megerősítés oldalra.</p>
|
visszamész az előző lépésre, majd visszajössz a Megerősítés oldalra.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC fizetés engedélyezve, koppints ide a részletekért.</string>
|
<string name="info_xmrto_enabled">BTC fizetés engedélyezve, koppints ide a részletekért.</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass engedélyezve, koppints ide a részletekért</string>
|
<string name="info_crazypass_enabled">CrAzYpass engedélyezve, koppints ide a részletekért</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger engedélyezve, koppints ide a részletekért</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Bitcoin-címet adtál meg.</b><br/>
|
<b>Bitcoin-címet adtál meg.</b><br/>
|
||||||
|
@ -44,7 +45,6 @@
|
||||||
|
|
||||||
<string name="info_send_xmrto_success_btc">%1$s BTC</string>
|
<string name="info_send_xmrto_success_btc">%1$s BTC</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="info_send_xmrto_paid">Megerősítés folyamatban</string>
|
<string name="info_send_xmrto_paid">Megerősítés folyamatban</string>
|
||||||
<string name="info_send_xmrto_unpaid">Kifizetés folyamatban</string>
|
<string name="info_send_xmrto_unpaid">Kifizetés folyamatban</string>
|
||||||
<string name="info_send_xmrto_error">XMR.TO-hiba (%1$s)</string>
|
<string name="info_send_xmrto_error">XMR.TO-hiba (%1$s)</string>
|
||||||
|
@ -322,4 +322,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Nyelv</string>
|
<string name="menu_language">Nyelv</string>
|
||||||
<string name="language_system_default">Rendszernyelv használata</string>
|
<string name="language_system_default">Rendszernyelv használata</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -177,4 +177,15 @@
|
||||||
<p>Non appena il conto alla rovescia arriva a zero, è necessario richiedere una nuova quotazione a XMR.TO tornando indietro al passo precedente e tornando poi di nuovo alla schermata \"Conferma\".</p>
|
<p>Non appena il conto alla rovescia arriva a zero, è necessario richiedere una nuova quotazione a XMR.TO tornando indietro al passo precedente e tornando poi di nuovo alla schermata \"Conferma\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Pagamento BTC abilitato, tocca per maggiori informazioni.</string>
|
<string name="info_xmrto_enabled">Pagamento BTC abilitato, tocca per maggiori informazioni.</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass abilitato, tocca per maggiori informazioni.</string>
|
<string name="info_crazypass_enabled">CrAzYpass abilitato, tocca per maggiori informazioni.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger abilitato, tocca per maggiori informazioni.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Hai inserito un indirizzo Bitcoin.</b><br/>
|
<b>Hai inserito un indirizzo Bitcoin.</b><br/>
|
||||||
|
@ -322,4 +323,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -233,4 +233,15 @@
|
||||||
det tidligere steget og komme tilbake til \"Bekreft\" skjermen.</p>
|
det tidligere steget og komme tilbake til \"Bekreft\" skjermen.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC betaling tilgjengelig, trykk for mer info.</string>
|
<string name="info_xmrto_enabled">BTC betaling tilgjengelig, trykk for mer info.</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass tilgjengelig, trykk for mer info.</string>
|
<string name="info_crazypass_enabled">CrAzYpass tilgjengelig, trykk for mer info.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger tilgjengelig, trykk for mer info.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Du skrev inn en Bitcoin addresse.</b><br/>
|
<b>Du skrev inn en Bitcoin addresse.</b><br/>
|
||||||
|
@ -320,4 +321,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -236,4 +236,15 @@
|
||||||
indo ao passo anterior e depois voltando ao ecrã de \"Confirmar\".</p>
|
indo ao passo anterior e depois voltando ao ecrã de \"Confirmar\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
<string name="info_send_prio_fees">Prioridade Alta = Taxas Altas</string>
|
<string name="info_send_prio_fees">Prioridade Alta = Taxas Altas</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Pagamento em BTC activado, toca para mais informação.</string>
|
<string name="info_xmrto_enabled">Pagamento em BTC activado, toca para mais informação.</string>
|
||||||
|
|
||||||
<string name="info_crazypass_enabled">passLoUCa activa, toca para mais informação.</string>
|
<string name="info_crazypass_enabled">passLoUCa activa, toca para mais informação.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger activa, toca para mais informação.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Introduziu um endereço Bitcoin.</b><br/>
|
<b>Introduziu um endereço Bitcoin.</b><br/>
|
||||||
|
@ -324,4 +324,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -221,4 +221,15 @@
|
||||||
înapoi la pasul anterior apoi revenind la ecranul \"Confirm\".</p>
|
înapoi la pasul anterior apoi revenind la ecranul \"Confirm\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
<string name="info_send_prio_fees">Prioritate mare = Comision mare</string>
|
<string name="info_send_prio_fees">Prioritate mare = Comision mare</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Plată BTC activată, apasă pentru mai multe informații.</string>
|
<string name="info_xmrto_enabled">Plată BTC activată, apasă pentru mai multe informații.</string>
|
||||||
|
<string name="info_crazypass_enabled">CrAzYpass activată, apasă pentru mai multe informații.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger activată, apasă pentru mai multe informații.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Ai introdus o adresă de Bitcoin.</b><br/>
|
<b>Ai introdus o adresă de Bitcoin.</b><br/>
|
||||||
|
@ -284,7 +286,6 @@
|
||||||
<string name="fab_restore_seed">Restaurează portofel folosind cele 25 de cuvinte mnemonice</string>
|
<string name="fab_restore_seed">Restaurează portofel folosind cele 25 de cuvinte mnemonice</string>
|
||||||
|
|
||||||
<string name="menu_changepw">Change Passphrase</string>
|
<string name="menu_changepw">Change Passphrase</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass enabled, tap for more info.</string>
|
|
||||||
<string name="changepw_progress">Change Password in progress</string>
|
<string name="changepw_progress">Change Password in progress</string>
|
||||||
<string name="changepw_failed">Change Password failed!</string>
|
<string name="changepw_failed">Change Password failed!</string>
|
||||||
<string name="changepw_success">Password changed</string>
|
<string name="changepw_success">Password changed</string>
|
||||||
|
@ -320,4 +321,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -239,4 +239,15 @@
|
||||||
предложение, вернувшись к предыдущему шагу, а затем к экрану \"Подтверждение\".</p>
|
предложение, вернувшись к предыдущему шагу, а затем к экрану \"Подтверждение\".</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
<string name="info_send_prio_fees">Высокий приоритет = Высокие комиссии</string>
|
<string name="info_send_prio_fees">Высокий приоритет = Высокие комиссии</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">Доступны переводы в BTC, нажмите для доп. информации</string>
|
<string name="info_xmrto_enabled">Доступны переводы в BTC, нажмите для доп. информации</string>
|
||||||
|
|
||||||
<string name="info_crazypass_enabled">Доступен CrAzYpass, нажмите для доп. информации</string>
|
<string name="info_crazypass_enabled">Доступен CrAzYpass, нажмите для доп. информации</string>
|
||||||
|
<string name="info_ledger_enabled">Доступен Ledger, нажмите для доп. информации</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>Вы ввели Bitcoin адрес.</b><br/>
|
<b>Вы ввели Bitcoin адрес.</b><br/>
|
||||||
|
@ -323,4 +323,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -214,4 +214,15 @@
|
||||||
tillbaka till den tidigare skärmen och sedan gå till \"Bekräfta\"skärmen.</p>
|
tillbaka till den tidigare skärmen och sedan gå till \"Bekräfta\"skärmen.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,8 +34,8 @@
|
||||||
<string name="info_send_prio_fees">Högre prioritet = Högre avgift</string>
|
<string name="info_send_prio_fees">Högre prioritet = Högre avgift</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC-betalning aktiverad, tryck för mer info.</string>
|
<string name="info_xmrto_enabled">BTC-betalning aktiverad, tryck för mer info.</string>
|
||||||
|
|
||||||
<string name="info_crazypass_enabled">CrAzYpass aktiverat, tryck för mer info.</string>
|
<string name="info_crazypass_enabled">CrAzYpass aktiverat, tryck för mer info.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger aktiverat, tryck för mer info.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[<b>Du har angivit en Bitcoin-adress.</b><br/> <i>Du kommer att skicka XMR och mottagaren får BTC via tjänsten <b>XMR.TO</b>.</i>]]></string>
|
<string name="info_xmrto"><![CDATA[<b>Du har angivit en Bitcoin-adress.</b><br/> <i>Du kommer att skicka XMR och mottagaren får BTC via tjänsten <b>XMR.TO</b>.</i>]]></string>
|
||||||
|
|
||||||
|
@ -305,4 +305,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -189,4 +189,15 @@
|
||||||
<p>当倒数计时归零的时候,你将会需要回到上一步再回到\"确认\"页面重新向XMR.TO寻求汇率报价</p>
|
<p>当倒数计时归零的时候,你将会需要回到上一步再回到\"确认\"页面重新向XMR.TO寻求汇率报价</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC付款已启用, 点选了解更多</string>
|
<string name="info_xmrto_enabled">BTC付款已启用, 点选了解更多</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass已启用, 点选了解更多</string>
|
<string name="info_crazypass_enabled">CrAzYpass已启用, 点选了解更多</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger已启用, 点选了解更多</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>你输入了Bitcoin地址</b><br/>
|
<b>你输入了Bitcoin地址</b><br/>
|
||||||
|
@ -318,4 +319,20 @@
|
||||||
|
|
||||||
<string name="menu_language">语言</string>
|
<string name="menu_language">语言</string>
|
||||||
<string name="language_system_default">使用系统语言</string>
|
<string name="language_system_default">使用系统语言</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -189,4 +189,15 @@
|
||||||
<p>當倒數計時歸零的時候,你將會需要回到上一步再回到\"確認\"頁面重新向XMR.TO尋求匯率報價</p>
|
<p>當倒數計時歸零的時候,你將會需要回到上一步再回到\"確認\"頁面重新向XMR.TO尋求匯率報價</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC付款已啟用, 點選了解更多</string>
|
<string name="info_xmrto_enabled">BTC付款已啟用, 點選了解更多</string>
|
||||||
<string name="info_crazypass_enabled">CrAzYpass已啟用, 點選了解更多</string>
|
<string name="info_crazypass_enabled">CrAzYpass已啟用, 點選了解更多</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger已啟用, 點選了解更多</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>你輸入了Bitcoin地址</b><br/>
|
<b>你輸入了Bitcoin地址</b><br/>
|
||||||
|
@ -319,4 +320,20 @@
|
||||||
|
|
||||||
<string name="menu_language">語言</string>
|
<string name="menu_language">語言</string>
|
||||||
<string name="language_system_default">使用系統語言</string>
|
<string name="language_system_default">使用系統語言</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -26,9 +26,21 @@
|
||||||
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
||||||
Use a strong password - even better use a passphrase.</p>
|
Use a strong password - even better use a passphrase.</p>
|
||||||
<p>Enter your Seed in the field \"Mnemonic Seed\".<p>
|
<p>Enter your Seed in the field \"Mnemonic Seed\".<p>
|
||||||
<p>If you know the block number of the first transaction used for this address, enter it in the
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
field \"Restore Height\" - leaving it blank will scan the <em>entire</em> blockchain for
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
transactions belonging to your address. This takes a <em>long</em> time.</p>
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
|
]]></string>
|
||||||
|
|
||||||
|
<string name="help_create_ledger"><![CDATA[
|
||||||
|
<h1>Create Wallet - Ledger</h1>
|
||||||
|
<p>You want to recover your wallet from your Ledger Nano S device.</p>
|
||||||
|
<p>Your secret keys never leave the Ledger device, so you need it plugged in every
|
||||||
|
time you want to access your wallet.</p>
|
||||||
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the Android
|
||||||
|
device. Use a strong password - even better use a passphrase.</p>
|
||||||
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<string name="help_create_keys"><![CDATA[
|
<string name="help_create_keys"><![CDATA[
|
||||||
|
@ -37,9 +49,9 @@
|
||||||
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
||||||
Use a strong password - even better use a passphrase.<p>
|
Use a strong password - even better use a passphrase.<p>
|
||||||
<p>Enter your Monero Address in the field \"Public Address\" and fill out \"View Key\" and \"Spend Key\".</p>
|
<p>Enter your Monero Address in the field \"Public Address\" and fill out \"View Key\" and \"Spend Key\".</p>
|
||||||
<p>If you know the block number of the first transaction used for this address, enter it in the
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
field \"Restore Height\" - leaving it blank will scan the <em>entire</em> blockchain for
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
transactions belonging to your address. This takes a <em>long</em> time.</p>
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<string name="help_create_view"><![CDATA[
|
<string name="help_create_view"><![CDATA[
|
||||||
|
@ -48,9 +60,9 @@
|
||||||
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
<p>Enter a unique wallet name and password. The password is used for securing your wallet data on the device.
|
||||||
Use a strong password - even better use a passphrase.<p>
|
Use a strong password - even better use a passphrase.<p>
|
||||||
<p>Enter your Monero Address in the field \"Public Address\" and fill out the \"View Key\".</p>
|
<p>Enter your Monero Address in the field \"Public Address\" and fill out the \"View Key\".</p>
|
||||||
<p>If you know the block number of the first transaction used for this address, enter it in the
|
<p>Enter the block number of the first transaction used for this address in the
|
||||||
field \"Restore Height\" - leaving it blank will scan the <em>entire</em> blockchain for
|
field \"Restore Height\". You can also use a date in the format YYYY-MM-DD. If you are not sure,
|
||||||
transactions belonging to your address. This takes a <em>long</em> time.</p>
|
enter an approximate date/blockheight <em>before</em> you first used this wallet address.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<string name="help_details"><![CDATA[
|
<string name="help_details"><![CDATA[
|
||||||
|
@ -69,7 +81,7 @@
|
||||||
If the password displayed here is 52 alphanumeric characters in groups of 4 - congratulations!
|
If the password displayed here is 52 alphanumeric characters in groups of 4 - congratulations!
|
||||||
Your wallet files are secured with a 256-bit key generated by your device's security
|
Your wallet files are secured with a 256-bit key generated by your device's security
|
||||||
features based on the passphrase you chose (on creation or by changing it). This makes it
|
features based on the passphrase you chose (on creation or by changing it). This makes it
|
||||||
extremenly difficult to hack!<br/>
|
extremely difficult to hack!<br/>
|
||||||
This feature is mandatory for all newly created wallets.
|
This feature is mandatory for all newly created wallets.
|
||||||
<h3>Legacy Password</h3>
|
<h3>Legacy Password</h3>
|
||||||
If you see your passphrase here, your wallet files are not as secure as when using
|
If you see your passphrase here, your wallet files are not as secure as when using
|
||||||
|
@ -232,5 +244,4 @@
|
||||||
<p>Once the countdown reaches zero, you need to get a new quote from XMR.TO by going back to the
|
<p>Once the countdown reaches zero, you need to get a new quote from XMR.TO by going back to the
|
||||||
previous step and then coming back to the \"Confirm\" screen.</p>
|
previous step and then coming back to the \"Confirm\" screen.</p>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
<string name="info_send_prio_fees">Higher Priority = Higher Fees</string>
|
<string name="info_send_prio_fees">Higher Priority = Higher Fees</string>
|
||||||
|
|
||||||
<string name="info_xmrto_enabled">BTC payment enabled, tap for more info.</string>
|
<string name="info_xmrto_enabled">BTC payment enabled, tap for more info.</string>
|
||||||
|
|
||||||
<string name="info_crazypass_enabled">CrAzYpass enabled, tap for more info.</string>
|
<string name="info_crazypass_enabled">CrAzYpass enabled, tap for more info.</string>
|
||||||
|
<string name="info_ledger_enabled">Ledger enabled, tap for more info.</string>
|
||||||
|
|
||||||
<string name="info_xmrto"><![CDATA[
|
<string name="info_xmrto"><![CDATA[
|
||||||
<b>You entered a Bitcoin address.</b><br/>
|
<b>You entered a Bitcoin address.</b><br/>
|
||||||
|
@ -47,7 +47,6 @@
|
||||||
|
|
||||||
<string name="info_send_xmrto_success_btc">%1$s BTC</string>
|
<string name="info_send_xmrto_success_btc">%1$s BTC</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="info_send_xmrto_paid">Confirmation Pending</string>
|
<string name="info_send_xmrto_paid">Confirmation Pending</string>
|
||||||
<string name="info_send_xmrto_unpaid">Payment Pending</string>
|
<string name="info_send_xmrto_unpaid">Payment Pending</string>
|
||||||
<string name="info_send_xmrto_error">XMR.TO Error (%1$s)</string>
|
<string name="info_send_xmrto_error">XMR.TO Error (%1$s)</string>
|
||||||
|
@ -209,6 +208,7 @@
|
||||||
<string name="generate_wallet_type_new">New</string>
|
<string name="generate_wallet_type_new">New</string>
|
||||||
<string name="generate_wallet_type_seed">Seed</string>
|
<string name="generate_wallet_type_seed">Seed</string>
|
||||||
<string name="generate_wallet_type_view">View</string>
|
<string name="generate_wallet_type_view">View</string>
|
||||||
|
<string name="generate_wallet_type_ledger" translatable="false">Ledger</string>
|
||||||
|
|
||||||
<string name="generate_address_hint">Public Address</string>
|
<string name="generate_address_hint">Public Address</string>
|
||||||
<string name="generate_viewkey_hint">View Key</string>
|
<string name="generate_viewkey_hint">View Key</string>
|
||||||
|
@ -368,4 +368,20 @@
|
||||||
|
|
||||||
<string name="menu_language">Language</string>
|
<string name="menu_language">Language</string>
|
||||||
<string name="language_system_default">Use System Language</string>
|
<string name="language_system_default">Use System Language</string>
|
||||||
|
|
||||||
|
<string name="fab_restore_ledger">Restore from Ledger Nano S</string>
|
||||||
|
|
||||||
|
<string name="progress_ledger_progress">Communicating with Ledger</string>
|
||||||
|
<string name="progress_ledger_confirm">Confirmation on Ledger required!</string>
|
||||||
|
<string name="progress_ledger_lookahead">Retrieving subaddresses</string>
|
||||||
|
<string name="progress_ledger_verify">Verifying keys</string>
|
||||||
|
<string name="progress_ledger_opentx">Doing crazy maths</string>
|
||||||
|
<string name="progress_ledger_mlsag">Hashing stuff</string>
|
||||||
|
<string name="open_wallet_ledger_missing">Please (re)connect Ledger device</string>
|
||||||
|
|
||||||
|
<string name="accounts_progress_new">Creating account</string>
|
||||||
|
<string name="accounts_progress_update">Updating wallet</string>
|
||||||
|
|
||||||
|
<string name="toast_ledger_attached">%1$s attached</string>
|
||||||
|
<string name="toast_ledger_detached">%1$s detached</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -329,5 +329,4 @@
|
||||||
<style name="MoneroSpinnerItemGray" parent="android:Widget.TextView.SpinnerItem">
|
<style name="MoneroSpinnerItemGray" parent="android:Widget.TextView.SpinnerItem">
|
||||||
<item name="android:textAppearance">@style/MoneroTextAppearanceSpinnerItemGray</item>
|
<item name="android:textAppearance">@style/MoneroTextAppearanceSpinnerItemGray</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<!-- Ledger Nano S HID: VID=0x2C97 PID=0x0001 -->
|
||||||
|
<usb-device
|
||||||
|
product-id="1"
|
||||||
|
vendor-id="11415" />
|
||||||
|
|
||||||
|
</resources>
|
|
@ -802,6 +802,12 @@ struct Wallet
|
||||||
|
|
||||||
//! Initiates a light wallet import wallet request
|
//! Initiates a light wallet import wallet request
|
||||||
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
|
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Queries if the wallet keys are on a hardware device
|
||||||
|
* \return true if they are
|
||||||
|
*/
|
||||||
|
virtual bool isKeyOnDevice() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -975,6 +981,17 @@ struct WalletManager
|
||||||
*/
|
*/
|
||||||
virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0;
|
virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief determine the key storage for the specified wallet file
|
||||||
|
* \param keys_file_name Keys file to verify password for
|
||||||
|
* \param password Password to verify
|
||||||
|
* \return -1: incorrect password, 0 = default hw, 1 ledger hw
|
||||||
|
*
|
||||||
|
* for verification only - determines key storage hardware
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual int queryWalletHardware(const std::string &keys_file_name, const std::string &password) const = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief findWallets - searches for the wallet files by given path name recursively
|
* \brief findWallets - searches for the wallet files by given path name recursively
|
||||||
* \param path - starting point to search
|
* \param path - starting point to search
|
||||||
|
|
Loading…
Reference in New Issue