1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-10 05:40:51 +02:00

Compare commits

..

28 Commits

Author SHA1 Message Date
m2049r
110057c294 bump version to 1.11.13 2019-07-14 13:18:27 +02:00
m2049r
7553d3c5f4 Merge pull request #611 from m2049r/fix_heightfix
reduce restore height some more for new wallets
2019-07-14 12:53:24 +02:00
m2049r
317976b34a decrease restore height on new wallet 2019-07-14 12:52:43 +02:00
m2049r
6ad423567f Merge pull request #610 from m2049r/feature_showheight
show restore height
2019-07-14 12:52:21 +02:00
m2049r
d497158856 show restore height 2019-07-14 12:35:00 +02:00
m2049r
f4cada5fa1 Merge pull request #608 from m2049r/feature_nanox
Support for Nano X
2019-07-13 18:56:23 +02:00
m2049r
352f0ad09c update height for 2019-07-01 (#609) 2019-07-13 18:55:35 +02:00
m2049r
ff1a9c1570 verify monero app is running on nano 2019-07-13 18:44:56 +02:00
m2049r
fa811a39a2 accept Nano X over USB 2019-07-13 13:00:24 +02:00
m2049r
cf5018be33 kick 'Hintergrunddienst' (#607) 2019-06-23 21:26:29 +02:00
m2049r
8ec027f9d4 bump version 2019-06-21 08:47:22 +02:00
m2049r
f28428e677 update june restore height (#606) 2019-06-21 08:45:09 +02:00
m2049r
294084bec5 double size of node bookmark icon (#605) 2019-06-21 08:17:08 +02:00
m2049r
4349907627 setNode blocks => call it async (#604) 2019-06-18 08:49:51 +02:00
m2049r
f7cef24a83 fix NPE (#603) 2019-06-18 08:48:28 +02:00
m2049r
2774f99b15 bump version v1.11.10 2019-06-16 23:39:24 +02:00
m2049r
bc630fc445 Refactor secureflag (#600)
* one place to decide if screenshots enabled

* allow screenshots on alpha or debug
2019-06-16 23:28:52 +02:00
m2049r
895cf16d33 don't setDaemon when null (#601) 2019-06-16 23:28:34 +02:00
m2049r
7f1796b12e bump version v1.11.9 2019-06-16 21:49:11 +02:00
erciccione
abe5c8afab readme: update contact info of the localization workgroup (#593) 2019-06-16 21:48:32 +02:00
WiserB
47f79b5269 Serbian translation of strings.xml (sr - made by WiserB) (#595)
helps.xml and about.xml included but not translated
2019-06-16 21:33:52 +02:00
erciccione
c9c07eaa15 Catalan translation (by ambystomamex) (#596) 2019-06-16 21:28:42 +02:00
m2049r
a490e3af0c better status & error message handling (#599) 2019-06-16 21:22:56 +02:00
m2049r
64d5b3bdea tweaks for monero v0.14.1.0 (#598) 2019-06-16 21:13:01 +02:00
m2049r
bf91eaf22f deal with not finding any text on clipboard (#594) 2019-05-24 23:44:54 +02:00
m2049r
2c3e73b540 bump version v1.11.7 2019-05-22 08:30:11 +02:00
m2049r
830d9dadb9 paste receiver address (#592) 2019-05-20 17:17:46 +02:00
m2049r
331d88ebba use daemon height for new wallets if available (#591) 2019-05-20 17:17:27 +02:00
64 changed files with 2266 additions and 605 deletions

File diff suppressed because one or more lines are too long

View File

@@ -137,7 +137,11 @@ set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
add_library(device_trezor STATIC IMPORTED)
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
@@ -147,6 +151,10 @@ add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
add_library(net STATIC IMPORTED)
set_target_properties(net PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
#############
# System
#############
@@ -166,6 +174,7 @@ target_link_libraries( monerujo
mnemonics
ringct
ringct_basic
net
common
cncrypto
blockchain_db
@@ -176,6 +185,7 @@ target_link_libraries( monerujo
blocks
checkpoints
device
device_trezor
multisig
version

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 28
versionCode 176
versionName "1.11.6 'Chernushka'"
versionCode 183
versionName "1.11.13 'Chernushka'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {

View File

@@ -1,88 +0,0 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#if defined(HAVE_MONERUJO)
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief LedgerFind - find Ledger Device and return it's name
* @param buffer - buffer for name of found device
* @param len - length of buffer
* @return 0 - success
* -1 - no device connected / found
* -2 - JVM not found
*/
int LedgerFind(char *buffer, size_t len);
/**
* @brief LedgerExchange - exchange data with Ledger Device
* @param command - buffer for data to send
* @param cmd_len - length of send to send
* @param response - buffer for received data
* @param max_resp_len - size of receive buffer
*
* @return length of received data in response or -1 if error
*/
int LedgerExchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
#ifdef __cplusplus
}
#endif
#include "device_io.hpp"
#pragma once
namespace hw {
namespace io {
class device_io_monerujo: device_io {
public:
device_io_monerujo() {};
~device_io_monerujo() {};
void init() {};
void release() {};
void connect(void *params) {};
void disconnect() {};
bool connected() const {return true;}; // monerujo is always connected before it gets here
// returns number of bytes read or -1 on error
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) {
return LedgerExchange(command, cmd_len, response, max_resp_len);
}
};
};
};
#endif //#if defined(HAVE_MONERUJO)

View File

@@ -39,6 +39,7 @@ static jclass class_WalletListener;
static jclass class_TransactionInfo;
static jclass class_Transfer;
static jclass class_Ledger;
static jclass class_WalletStatus;
std::mutex _listenerMutex;
@@ -61,6 +62,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
class_Ledger = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
class_WalletStatus = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status")));
return JNI_VERSION_1_6;
}
#ifdef __cplusplus
@@ -581,10 +584,25 @@ Java_com_m2049r_xmrwallet_model_Wallet_getStatusJ(JNIEnv *env, jobject instance)
return wallet->status();
}
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getErrorString(JNIEnv *env, jobject instance) {
jobject newWalletStatusInstance(JNIEnv *env, int status, const std::string &errorString) {
jmethodID init = env->GetMethodID(class_WalletStatus, "<init>",
"(ILjava/lang/String;)V");
jstring _errorString = env->NewStringUTF(errorString.c_str());
jobject instance = env->NewObject(class_WalletStatus, init, status, _errorString);
env->DeleteLocalRef(_errorString);
return instance;
}
JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_statusWithErrorString(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return env->NewStringUTF(wallet->errorString().c_str());
int status;
std::string errorString;
wallet->statusWithErrorString(status, errorString);
return newWalletStatusInstance(env, status, errorString);
}
JNIEXPORT jboolean JNICALL
@@ -1292,7 +1310,6 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxIdJ(JNIEnv *env, jo
return nullptr;
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobject instance) {
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);
@@ -1378,12 +1395,15 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jclass cl
Bitmonero::WalletManagerFactory::setLogLevel(level);
}
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_moneroVersion(JNIEnv *env, jclass clazz) {
return env->NewStringUTF(MONERO_VERSION);
}
//
// Ledger Stuff
//
#include "device_io_monerujo.hpp"
/**
* @brief LedgerExchange - exchange data with Ledger Device
* @param command - buffer for data to send
@@ -1417,7 +1437,7 @@ int LedgerExchange(
return -1;
}
jsize len = jenv->GetArrayLength(dataRecv);
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cmd_len, len);
LOGD("LedgerExchange SCARD_S_SUCCESS %u/%d", cmd_len, len);
if (len <= max_resp_len) {
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) response);
jenv->DeleteLocalRef(dataRecv);

View File

@@ -54,6 +54,8 @@ extern "C"
{
#endif
extern const char* const MONERO_VERSION; // the actual monero core version
// from monero-core crypto/hash-ops.h - avoid #including monero code here
enum {
HASH_SIZE = 32,

View File

@@ -46,8 +46,12 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
for (UsbDevice device : deviceList.values()) {
Timber.d("%04X:%04X %s, %s", device.getVendorId(), device.getProductId(), device.getManufacturerName(), device.getProductName());
if ((device.getVendorId() == VID) && (device.getProductId() == PID_HID)) {
return device;
if (device.getVendorId() == VID) {
final int deviceProductId = device.getProductId();
for (int pid : PID_HIDS) {
if (deviceProductId == pid)
return device;
}
}
}
return null;
@@ -74,7 +78,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
}
private static final int VID = 0x2C97;
private static final int PID_HID = 0x0001;
private static final int[] PID_HIDS = {0x0001, 0x0004};
private UsbDeviceConnection connection;
private UsbInterface dongleInterface;

View File

@@ -682,8 +682,7 @@ public class GenerateFragment extends Fragment {
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
ledgerDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

View File

@@ -55,6 +55,8 @@ import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
import timber.log.Timber;
public class GenerateReviewFragment extends Fragment {
@@ -72,6 +74,7 @@ public class GenerateReviewFragment extends Fragment {
private TextView tvWalletPassword;
private TextView tvWalletAddress;
private TextView tvWalletMnemonic;
private TextView tvWalletHeight;
private TextView tvWalletViewKey;
private TextView tvWalletSpendKey;
private ImageButton bCopyAddress;
@@ -99,6 +102,7 @@ public class GenerateReviewFragment extends Fragment {
tvWalletViewKey = view.findViewById(R.id.tvWalletViewKey);
tvWalletSpendKey = view.findViewById(R.id.tvWalletSpendKey);
tvWalletMnemonic = view.findViewById(R.id.tvWalletMnemonic);
tvWalletHeight = view.findViewById(R.id.tvWalletHeight);
bCopyAddress = view.findViewById(R.id.bCopyAddress);
bAdvancedInfo = view.findViewById(R.id.bAdvancedInfo);
llAdvancedInfo = view.findViewById(R.id.llAdvancedInfo);
@@ -188,11 +192,12 @@ public class GenerateReviewFragment extends Fragment {
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
String name;
String address;
long height;
String seed;
String viewKey;
String spendKey;
boolean isWatchOnly;
Wallet.Status status;
Wallet.Status walletStatus;
boolean dialogOpened = false;
@@ -224,14 +229,15 @@ public class GenerateReviewFragment extends Fragment {
closeWallet = true;
}
name = wallet.getName();
status = wallet.getStatus();
if (status != Wallet.Status.Status_Ok) {
Timber.e(wallet.getErrorString());
walletStatus = wallet.getStatus();
if (!walletStatus.isOk()) {
Timber.e(walletStatus.getErrorString());
if (closeWallet) wallet.close();
return false;
}
address = wallet.getAddress();
height = wallet.getRestoreHeight();
seed = wallet.getSeed();
switch (wallet.getDeviceType()) {
case Device_Ledger:
@@ -264,6 +270,7 @@ public class GenerateReviewFragment extends Fragment {
llPassword.setVisibility(View.VISIBLE);
tvWalletPassword.setText(getPassword());
tvWalletAddress.setText(address);
tvWalletHeight.setText(NumberFormat.getInstance().format(height));
if (!seed.isEmpty()) {
llMnemonic.setVisibility(View.VISIBLE);
tvWalletMnemonic.setText(seed);
@@ -287,10 +294,11 @@ public class GenerateReviewFragment extends Fragment {
GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK);
} else {
// TODO show proper error message and/or end the fragment?
tvWalletAddress.setText(status.toString());
tvWalletMnemonic.setText(status.toString());
tvWalletViewKey.setText(status.toString());
tvWalletSpendKey.setText(status.toString());
tvWalletAddress.setText(walletStatus.toString());
tvWalletHeight.setText(walletStatus.toString());
tvWalletMnemonic.setText(walletStatus.toString());
tvWalletViewKey.setText(walletStatus.toString());
tvWalletSpendKey.setText(walletStatus.toString());
}
hideProgress();
}
@@ -414,12 +422,13 @@ public class GenerateReviewFragment extends Fragment {
}
boolean ok = false;
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
Wallet.Status walletStatus = wallet.getStatus();
if (walletStatus.isOk()) {
wallet.setPassword(newPassword);
wallet.store();
ok = true;
} else {
Timber.e(wallet.getErrorString());
Timber.e(walletStatus.getErrorString());
}
if (closeWallet) wallet.close();
return ok;
@@ -621,8 +630,8 @@ public class GenerateReviewFragment extends Fragment {
return false;
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

View File

@@ -918,6 +918,16 @@ public class LoginActivity extends BaseActivity
}
boolean checkAndCloseWallet(Wallet aWallet) {
Wallet.Status walletStatus = aWallet.getStatus();
if (!walletStatus.isOk()) {
Timber.e(walletStatus.getErrorString());
toast(walletStatus.getErrorString());
}
aWallet.close();
return walletStatus.isOk();
}
@Override
public void onGenerate(final String name, final String password) {
createWallet(name, password,
@@ -929,15 +939,13 @@ public class LoginActivity extends BaseActivity
@Override
public boolean createWallet(File aFile, String password) {
NodeInfo currentNode = getNode();
// get it from the connected node if we have one, and go back ca. 4 days
final long restoreHeight =
(currentNode != null) ? currentNode.getHeight() - 2000 : -1;
Wallet newWallet = WalletManager.getInstance()
.createWallet(aFile, password, MNEMONIC_LANGUAGE);
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
newWallet.close();
return success;
.createWallet(aFile, password, MNEMONIC_LANGUAGE, restoreHeight);
return checkAndCloseWallet(newWallet);
}
});
}
@@ -956,13 +964,7 @@ public class LoginActivity extends BaseActivity
public boolean createWallet(File aFile, String password) {
Wallet newWallet = WalletManager.getInstance()
.recoveryWallet(aFile, password, seed, restoreHeight);
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
newWallet.close();
return success;
return checkAndCloseWallet(newWallet);
}
});
}
@@ -982,13 +984,7 @@ public class LoginActivity extends BaseActivity
Wallet newWallet = WalletManager.getInstance()
.createWalletFromDevice(aFile, password,
restoreHeight, "Ledger");
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
newWallet.close();
return success;
return checkAndCloseWallet(newWallet);
}
});
}
@@ -1009,13 +1005,7 @@ public class LoginActivity extends BaseActivity
Wallet newWallet = WalletManager.getInstance()
.createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight,
address, viewKey, spendKey);
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
newWallet.close();
return success;
return checkAndCloseWallet(newWallet);
}
});
}
@@ -1370,17 +1360,30 @@ public class LoginActivity extends BaseActivity
if (Ledger.ENABLED)
try {
Ledger.connect(usbManager, usbDevice);
registerDetachReceiver();
onLedgerAction();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this,
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
Toast.LENGTH_SHORT)
.show();
}
});
if (!Ledger.check()) {
Ledger.disconnect();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this,
getString(R.string.toast_ledger_start_app, usbDevice.getProductName()),
Toast.LENGTH_SHORT)
.show();
}
});
} else {
registerDetachReceiver();
onLedgerAction();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this,
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
Toast.LENGTH_SHORT)
.show();
}
});
}
} catch (IOException ex) {
runOnUiThread(new Runnable() {
@Override

View File

@@ -439,10 +439,13 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
NodeInfo bestNode = nodesToTest.get(0);
if (bestNode.isValid())
return nodesToTest.get(0);
else
if (bestNode.isValid()) {
activityCallback.setNode(bestNode);
return bestNode;
} else {
activityCallback.setNode(null);
return null;
}
}
@Override
@@ -450,7 +453,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
if (!isAdded()) return;
pbNode.setVisibility(View.INVISIBLE);
llNode.setVisibility(View.VISIBLE);
activityCallback.setNode(result);
if (result != null) {
Timber.d("found a good node %s", result.toString());
showNode(result);

View File

@@ -506,8 +506,8 @@ public class NodeFragment extends Fragment
});
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

View File

@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.LocaleHelper;
import static android.view.WindowManager.LayoutParams;
@@ -30,8 +31,7 @@ public abstract class SecureActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
}
}

View File

@@ -628,23 +628,22 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
}
@Override
public void onWalletStarted(final Wallet.ConnectionStatus connStatus) {
public void onWalletStarted(final Wallet.Status walletStatus) {
runOnUiThread(new Runnable() {
public void run() {
dismissProgressDialog();
switch (connStatus) {
case ConnectionStatus_Disconnected:
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
break;
case ConnectionStatus_WrongVersion:
if (walletStatus == null) {
// guess what went wrong
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
} else {
if (Wallet.ConnectionStatus.ConnectionStatus_WrongVersion == walletStatus.getConnectionStatus())
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_wrongversion), Toast.LENGTH_LONG).show();
break;
case ConnectionStatus_Connected:
break;
else if (!walletStatus.isOk())
Toast.makeText(WalletActivity.this, walletStatus.getErrorString(), Toast.LENGTH_LONG).show();
}
}
});
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
if ((walletStatus == null) || (Wallet.ConnectionStatus.ConnectionStatus_Connected != walletStatus.getConnectionStatus())) {
finish();
} else {
haveWallet = true;

View File

@@ -28,6 +28,7 @@ import android.widget.TextView;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;
import java.util.Locale;
@@ -76,8 +77,7 @@ public class ProgressDialog extends AlertDialog {
super.onCreate(savedInstanceState);
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
}

View File

@@ -33,7 +33,9 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.BarcodeData;
@@ -92,6 +94,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private View llPaymentId;
private TextView tvXmrTo;
private View llXmrTo;
private ImageButton bPasteAddress;
private boolean resolvingOA = false;
private boolean resolvingPP = false;
@@ -197,6 +200,21 @@ public class SendAddressWizardFragment extends SendWizardFragment {
}
});
bPasteAddress = view.findViewById(R.id.bPasteAddress);
bPasteAddress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String clip = Helper.getClipBoardText(getActivity());
if (clip == null) return;
// clean it up
final String address = clip.replaceAll("[^0-9A-Z-a-z]", "");
if (Wallet.isAddressValid(address) || BitcoinAddressValidator.validate(address))
etAddress.getEditText().setText(address);
else
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
}
});
etPaymentId = view.findViewById(R.id.etPaymentId);
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {

View File

@@ -437,8 +437,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
return false;
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

View File

@@ -324,8 +324,8 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
return false;
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

View File

@@ -134,6 +134,10 @@ public enum Instruction {
return value;
}
public byte getByteValue() {
return (byte) (value & 0xFF);
}
private int value;
Instruction(int value) {

View File

@@ -27,9 +27,11 @@ import com.btchip.BTChipException;
import com.btchip.comm.BTChipTransport;
import com.btchip.comm.android.BTChipTransportAndroidHID;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import timber.log.Timber;
@@ -40,9 +42,11 @@ public class Ledger {
static public final int LOOKAHEAD_SUBADDRESSES = 20;
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
private static final byte PROTOCOL_VERSION = 0x02;
public static final int SW_OK = 0x9000;
public static final int SW_INS_NOT_SUPPORTED = 0x6D00;
public static final int OK[] = {SW_OK};
public static final int MINIMUM_LEDGER_VERSION = (1 << 16) + (3 << 8) + (1); // 1.3.1
public static UsbDevice findDevice(UsbManager usbManager) {
if (!ENABLED) return null;
@@ -89,6 +93,21 @@ public class Ledger {
}
}
static public boolean check() {
if (Instance == null) return false;
byte[] moneroVersion = WalletManager.moneroVersion().getBytes(StandardCharsets.US_ASCII);
try {
byte[] resp = Instance.exchangeApduNoOpt(Instruction.INS_RESET, moneroVersion, OK);
int deviceVersion = (resp[0] << 16) + (resp[1] << 8) + (resp[2]);
if (deviceVersion < MINIMUM_LEDGER_VERSION)
return false;
} catch (BTChipException ex) { // comm error - probably wrong app started on device
return false;
}
return true;
}
final private BTChipTransport transport;
final private String name;
private int lastSW = 0;
@@ -112,7 +131,7 @@ public class Ledger {
synchronized private byte[] exchangeRaw(byte[] apdu) {
if (transport == null)
throw new IllegalStateException("No transport (probably closed previously)");
Timber.i("exchangeRaw %02x", apdu[1]);
Timber.d("exchangeRaw %02x", apdu[1]);
Instruction ins = Instruction.fromByte(apdu[1]);
if (listener != null) listener.onInstructionSend(ins, apdu);
sniffOut(ins, apdu);
@@ -120,7 +139,6 @@ public class Ledger {
if (listener != null) listener.onInstructionReceive(ins, data);
sniffIn(data);
return data;
}
private byte[] exchange(byte[] apdu) throws BTChipException {
@@ -148,68 +166,19 @@ public class Ledger {
throw new BTChipException("Invalid status", lastSW);
}
private byte[] exchangeApdu(byte cla, byte ins, byte p1, byte p2, byte[] data, int acceptedSW[]) throws BTChipException {
byte[] apdu = new byte[data.length + 5];
apdu[0] = cla;
apdu[1] = ins;
apdu[2] = p1;
apdu[3] = p2;
apdu[4] = (byte) (data.length);
System.arraycopy(data, 0, apdu, 5, data.length);
private byte[] exchangeApduNoOpt(Instruction instruction, byte[] data, int acceptedSW[])
throws BTChipException {
byte[] apdu = new byte[data.length + 6];
apdu[0] = PROTOCOL_VERSION;
apdu[1] = instruction.getByteValue();
apdu[2] = 0; // p1
apdu[3] = 0; // p2
apdu[4] = (byte) (data.length + 1); // +1 because the opt byte is part of the data
apdu[5] = 0; // opt
System.arraycopy(data, 0, apdu, 6, data.length);
return exchangeCheck(apdu, acceptedSW);
}
private byte[] exchangeApdu(byte cla, byte ins, byte p1, byte p2, int length, int acceptedSW[]) throws BTChipException {
byte[] apdu = new byte[5];
apdu[0] = cla;
apdu[1] = ins;
apdu[2] = p1;
apdu[3] = p2;
apdu[4] = (byte) (length);
return exchangeCheck(apdu, acceptedSW);
}
private byte[] exchangeApduSplit(byte cla, byte ins, byte p1, byte p2, byte[] data, int acceptedSW[]) throws BTChipException {
int offset = 0;
byte[] result = null;
while (offset < data.length) {
int blockLength = ((data.length - offset) > 255 ? 255 : data.length - offset);
byte[] apdu = new byte[blockLength + 5];
apdu[0] = cla;
apdu[1] = ins;
apdu[2] = p1;
apdu[3] = p2;
apdu[4] = (byte) (blockLength);
System.arraycopy(data, offset, apdu, 5, blockLength);
result = exchangeCheck(apdu, acceptedSW);
offset += blockLength;
}
return result;
}
private byte[] exchangeApduSplit2(byte cla, byte ins, byte p1, byte p2, byte[] data, byte[] data2, int acceptedSW[]) throws BTChipException {
int offset = 0;
byte[] result = null;
int maxBlockSize = 255 - data2.length;
while (offset < data.length) {
int blockLength = ((data.length - offset) > maxBlockSize ? maxBlockSize : data.length - offset);
boolean lastBlock = ((offset + blockLength) == data.length);
byte[] apdu = new byte[blockLength + 5 + (lastBlock ? data2.length : 0)];
apdu[0] = cla;
apdu[1] = ins;
apdu[2] = p1;
apdu[3] = p2;
apdu[4] = (byte) (blockLength + (lastBlock ? data2.length : 0));
System.arraycopy(data, offset, apdu, 5, blockLength);
if (lastBlock) {
System.arraycopy(data2, 0, apdu, 5 + blockLength, data2.length);
}
result = exchangeCheck(apdu, acceptedSW);
offset += blockLength;
}
return result;
}
public interface Listener {
void onInstructionSend(Instruction ins, byte[] apdu);
@@ -251,7 +220,6 @@ public class Ledger {
if (ins == Instruction.INS_GET_KEY) {
snoopKey = (apdu[2] == 2);
}
}
private void sniffIn(byte[] data) {

View File

@@ -16,6 +16,9 @@
package com.m2049r.xmrwallet.model;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.m2049r.xmrwallet.data.TxData;
import java.io.File;
@@ -32,6 +35,47 @@ public class Wallet {
System.loadLibrary("monerujo");
}
static public class Status {
Status(int status, String errorString) {
this.status = StatusEnum.values()[status];
this.errorString = errorString;
}
final private StatusEnum status;
final private String errorString;
@Nullable
private ConnectionStatus connectionStatus; // optional
public StatusEnum getStatus() {
return status;
}
public String getErrorString() {
return errorString;
}
public void setConnectionStatus(@Nullable ConnectionStatus connectionStatus) {
this.connectionStatus = connectionStatus;
}
@Nullable
public ConnectionStatus getConnectionStatus() {
return connectionStatus;
}
public boolean isOk() {
return (getStatus() == StatusEnum.Status_Ok)
&& ((getConnectionStatus() == null) ||
(getConnectionStatus() == ConnectionStatus.ConnectionStatus_Connected));
}
@Override
@NonNull
public String toString() {
return "Wallet.Status: (" + status + "/" + errorString + ", " + connectionStatus;
}
}
private int accountIndex = 0;
public int getAccountIndex() {
@@ -66,7 +110,7 @@ public class Wallet {
Device_Ledger
}
public enum Status {
public enum StatusEnum {
Status_Ok,
Status_Error,
Status_Critical
@@ -85,12 +129,16 @@ public class Wallet {
public native void setSeedLanguage(String language);
public Status getStatus() {
return Wallet.Status.values()[getStatusJ()];
return statusWithErrorString();
}
private native int getStatusJ();
public Status getFullStatus() {
Wallet.Status walletStatus = statusWithErrorString();
walletStatus.setConnectionStatus(getConnectionStatus());
return walletStatus;
}
public native String getErrorString();
private native Status statusWithErrorString();
public native boolean setPassword(String password);

View File

@@ -91,14 +91,16 @@ public class WalletManager {
managedWallet = null;
}
public Wallet createWallet(File aFile, String password, String language) {
public Wallet createWallet(File aFile, String password, String language, long height) {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet);
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
if (wallet.getStatus().isOk()) {
// (Re-)Estimate restore height based on what we know
long oldHeight = wallet.getRestoreHeight();
wallet.setRestoreHeight(RestoreHeight.getInstance().getHeight(new Date()));
final long oldHeight = wallet.getRestoreHeight();
final long restoreHeight =
(height > -1) ? height : RestoreHeight.getInstance().getHeight(new Date());
wallet.setRestoreHeight(restoreHeight);
Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight());
wallet.setPassword(password); // this rewrites the keys file (which contains the restore height)
}
@@ -285,7 +287,8 @@ public class WalletManager {
this.daemonAddress = null;
this.daemonUsername = "";
this.daemonPassword = "";
setDaemonAddressJ("");
//setDaemonAddressJ(""); // don't disconnect as monero code blocks for many seconds!
//TODO: need to do something about that later
}
}
@@ -351,4 +354,6 @@ public class WalletManager {
static public native void logWarning(String category, String message);
static public native void logError(String category, String message);
static public native String moneroVersion();
}

View File

@@ -31,6 +31,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
@@ -220,7 +221,7 @@ public class WalletService extends Service {
void onSendTransactionFailed(String error);
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
void onWalletStarted(Wallet.Status walletStatus);
void onWalletOpen(Wallet.Device device);
}
@@ -287,9 +288,9 @@ public class WalletService extends Service {
if (walletId != null) {
showProgress(getString(R.string.status_wallet_loading));
showProgress(10);
Wallet.ConnectionStatus connStatus = start(walletId, walletPw);
if (observer != null) observer.onWalletStarted(connStatus);
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
Wallet.Status walletStatus = start(walletId, walletPw);
if (observer != null) observer.onWalletStarted(walletStatus);
if ((walletStatus == null) || !walletStatus.isOk()) {
errorState = true;
stop();
}
@@ -300,7 +301,7 @@ public class WalletService extends Service {
boolean rc = myWallet.store();
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
if (!rc) {
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
}
if (observer != null) observer.onWalletStored(rc);
} else if (cmd.equals(REQUEST_CMD_TX)) {
@@ -362,7 +363,7 @@ public class WalletService extends Service {
boolean rc = myWallet.store();
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
if (!rc) {
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
}
if (observer != null) observer.onWalletStored(rc);
listener.updated = true;
@@ -464,7 +465,8 @@ public class WalletService extends Service {
return true; // true is important so that onUnbind is also called next time
}
private Wallet.ConnectionStatus start(String walletName, String walletPassword) {
@Nullable
private Wallet.Status start(String walletName, String walletPassword) {
Timber.d("start()");
startNotfication();
showProgress(getString(R.string.status_wallet_loading));
@@ -472,11 +474,11 @@ public class WalletService extends Service {
if (listener == null) {
Timber.d("start() loadWallet");
Wallet aWallet = loadWallet(walletName, walletPassword);
Wallet.ConnectionStatus connStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
if (aWallet != null) connStatus = aWallet.getConnectionStatus();
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
if (aWallet != null) aWallet.close();
return connStatus;
if (aWallet == null) return null;
Wallet.Status walletStatus = aWallet.getFullStatus();
if (!walletStatus.isOk()) {
aWallet.close();
return walletStatus;
}
listener = new MyWalletListener();
listener.start();
@@ -487,7 +489,7 @@ public class WalletService extends Service {
// if we try to refresh the history here we get occasional segfaults!
// doesnt matter since we update as soon as we get a new block anyway
Timber.d("start() done");
return Wallet.ConnectionStatus.ConnectionStatus_Connected;
return getWallet().getFullStatus();
}
public void stop() {
@@ -532,10 +534,9 @@ public class WalletService extends Service {
wallet = walletMgr.openWallet(path, walletPassword);
showProgress(60);
Timber.d("wallet opened");
Wallet.Status status = wallet.getStatus();
Timber.d("wallet status is %s", status);
if (status != Wallet.Status.Status_Ok) {
Timber.d("wallet status is %s", status);
Wallet.Status walletStatus = wallet.getStatus();
if (!walletStatus.isOk()) {
Timber.d("wallet status is %s", walletStatus);
WalletManager.getInstance().close(wallet); // TODO close() failed?
wallet = null;
// TODO what do we do with the progress??

View File

@@ -21,6 +21,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -281,6 +282,21 @@ public class Helper {
clipboardManager.setPrimaryClip(clip);
}
static public String getClipBoardText(Context context) {
final ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
try {
if (clipboardManager.hasPrimaryClip()
&& clipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
final ClipData.Item item = clipboardManager.getPrimaryClip().getItemAt(0);
return item.getText().toString();
}
} catch (NullPointerException ex) {
// if we have don't find a text in the clipboard
return null;
}
return null;
}
static private Animation ShakeAnimation;
static public Animation getShakeAnimation(Context context) {
@@ -585,8 +601,7 @@ public class Helper {
}
});
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
if (Helper.preventScreenshot()) {
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
@@ -626,4 +641,8 @@ public class Helper {
StrictMode.setThreadPolicy(currentPolicy);
}
}
static public boolean preventScreenshot() {
return !(BuildConfig.DEBUG || BuildConfig.FLAVOR_type.equals("alpha"));
}
}

View File

@@ -103,6 +103,8 @@ public class RestoreHeight {
blockheight.put("2019-03-01", 1781681L);
blockheight.put("2019-04-01", 1803081L);
blockheight.put("2019-05-01", 1824671L);
blockheight.put("2019-06-01", 1847005L);
blockheight.put("2019-07-01", 1868590L);
}
public long getHeight(String date) {

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/gradientOrange"
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z" />
</vector>

View File

@@ -77,6 +77,32 @@
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
android:id="@+id/llHeight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/data_top"
android:orientation="vertical"
android:visibility="visible">
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:text="@string/label_restoreheight" />
<TextView
android:id="@+id/tvWalletHeight"
style="@style/MoneroText.Monospace.Mnemonic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/data_top"
android:background="@drawable/backgound_seed"
android:textAlignment="center"
tools:text="1,878,151" />
</LinearLayout>
<LinearLayout
android:id="@+id/llPassword"
android:layout_width="match_parent"

View File

@@ -20,21 +20,46 @@
android:layout_width="0dp"
android:layout_height="0dp" />
<android.support.design.widget.TextInputLayout
android:id="@+id/etAddress"
<RelativeLayout
android:id="@+id/llAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
android:layout_gravity="center"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
<android.support.design.widget.TextInputLayout
android:id="@+id/etAddress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/send_address_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/bPasteAddress"
app:counterEnabled="true"
app:counterMaxLength="16"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="10"
android:hint="@string/send_address_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
<ImageButton
android:id="@+id/bPasteAddress"
style="@style/MoneroText.Button.Small"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentEnd="true"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:background="?android:selectableItemBackgroundBorderless"
android:src="@drawable/ic_content_paste_orange_24dp" />
</RelativeLayout>
<FrameLayout
android:layout_width="match_parent"

View File

@@ -15,6 +15,7 @@
android:layout_centerInParent="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:padding="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:src="@drawable/ic_bookmark_border_24dp" />
@@ -24,6 +25,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:layout_toStartOf="@+id/ivPing"
android:layout_toEndOf="@id/ibBookmark"

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about_close">Tancar</string>
<string name="about_whoami">Sóc en monerujo</string>
<string name="about_version">Versió %1$s (%2$d)</string>
<string name="credits_text"><![CDATA[
<b>Credits</b>
<br/>
m2049r, baltsar777, anhdres, keejef,
rehrar, EarlOfEgo, ErCiccione et al.
<br/><br/>
<a href="https://monerujo.io/">monerujo.io</a>
]]></string>
<string name="privacy_policy"><![CDATA[
<h1>Política de Privacitat</h1>
<p>Aquesta pàgina us informa de les nostres polítiques relatives a la recopilació,
         ús i divulgació de la informació personal que rebem dels usuaris de la nostra
         aplicació (monerujo: Monero Wallet).
</p>
<p>Mitjançant l'ús d'aquesta aplicació vostè accepta la recopilació i lús de la informació dacord amb aquesta política.
</p>
<h2>Dades Recopilades</h2>
<p>Les dades personals són qualsevol tipus de dades que puguin identificar un individu.
</p>
<p>Les claus i les adreces públiques de Monero són recollides i processades per laplicació de forma local per tal de processar les transaccions i transmetre-les a la xarxa Monero de forma encriptada.
</p>
<p>L'aplicació no recopila altres dades personals.</p>
<p>Si utilitzeu la funcionalitat d'intercanvi (opcional), monerujo obté el canvi
         a través de lAPI pública de coinmarketcap.com.
         Consulteu la seva política de privacitat a https://coinmarketcap.com/privacy per a més detalls sobre com es recullen les dades de les vostres peticions</p>
<p>Si utilitzeu laplicació per pagar a adreces BTC, utilitzareu el servei XMR.TO.
         Consulteu la seva política de privacitat a https://xmr.to/ per obtenir més informació. Monerujo els envia l'adreça de destinació de BTC i la quantitat. La vostra IP també podrà ser recollida.</p>
<h2>Permisos de la App</h2>
<ul>
<li>INTERNET : Connectar a la xarxa Monero mitjançant un Node Daemon de Monero</li>
<li>READ_EXTERNAL_STORAGE : Llegir els arxius del portamonedes emmagatzemats al dispositiu</li>
<li>WRITE_EXTERNAL_STORAGE : Escriure els arxius del portamonedes emmagatzemats al dispositiu</li>
<li>WAKE_LOCK : Mantenir el dispositiu despert durant la sincronització</li>
<li>CAMERA : Escanejar codis QR per rebre Monero</li>
</ul>
<h2>Canvis en aquesta Política de Privacitat</h2>
<p>És possible que actualitzem aquesta política de privacitat de tant en tant. Li notificarem de qualsevol canvi publicant la nova política de privacitat a laplicació i al lloc web (www.monerujo.io)
         Us recomanem que reviseu aquesta política de privacitat periòdicament per conèixer els canvis.
<p>Aquesta Política de Privacitat es va actualitzar per darrer cop: 10th November, 2017.
</p>
<h2>Contacti amb nosaltres</h2>
<p>Si teniu alguna consulta sobre la nostra política de privadesa,
         o com es recullen i processen les vostres dades,
         si us plau envieu un correu electrònic a privacy@monerujo.io.
</p>
]]></string>
</resources>

View File

@@ -0,0 +1,244 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="help_create_new"><![CDATA[
<h1>Crear Portamonedes - New</h1>
<p>Per si necessiteu una nova adreça de Monero!</p>
<p>Introduïu un nom i contrasenya únics del portamonedes.
         La contrasenya sutilitza per protegir les dades del seu portamonedes en el dispositiu. Utilitzeu una contrasenya sòlida - encara millor si utiliza una frase.</p>
<h2>Apunteu la vostra llavor mnemotècnica!</h2>
<p>A la pantalla següent trobareu la vostra "Llavor Mnemotècnica" de 25 paraules.
         Aquestes són les úniques dades necessàries per a recuperar el vostre portamonedes i obtenir accés total als vostres fons.
         Mantenir-la segura i privada és molt important ja que permet que <em> qualsevol persona</em> tingui accés total als vostres fons!.</p>
<p>Si perdeu la contrasenya del portamonedes encara podrieu recuperar-lo amb la llavor Mnemotècnica.</p>
<p>La llavor Mnemotècnica tampoc no es pot canviar mai, i si és robada o compromesa d'alguna manera haureu de traslladar els vostres fons a un nou portamonedes (amb una nova llavor mnemotècnica). Per tant, es recomana que feu còpies de seguretat de la vostra llavor mnemotècnica escrivint-la i emmagatzemant-la a <em> múltiple </em> llocs de forma segura.</p>
]]></string>
<string name="help_create_seed"><![CDATA[
<h1>Crear Portamonedes - Llavor</h1>
<p>Si ja teniu una adreça Monero i voleu recuperar les transaccions de la blockchain!</p>
<p>Introduïu un nom i contrasenya únics del portamonedes.
         La contrasenya sutilitza per protegir les dades del seu portamonedes en el dispositiu. Utilitzeu una contrasenya sòlida - encara millor si utiliza una frase.</p>
<p>Introduïu la Llavor en el camp \"Llavor Mnemotècnica\".<p>
<p>Introduïu el número de bloc de la primera transacció realitzada en aquesta adreça en el camp "Restaurar Alçada". També podeu utilitzar una data en el format AAAA-MM-DD. Si no esteu segurs, introduïu una data / alçada de bloc aproximada <em> abans </em> d'haver fer servir aquesta adreça del portamonedes.</p>
]]></string>
<string name="help_create_ledger"><![CDATA[
<h1>Crear Portamonedes - Ledger</h1>
<p>Si voleu recuperar el vostre portamonedes desde un dispositiu Ledger Nano S!</p>
<p>Les vostres claus secretes no surten mai del dispositiu Ledger, de manera que necessiteu que estigui connectat sempre que vulgueu accedir aal vostre portamonedes.</p>
<p>Introduïu un nom i contrasenya únics del portamonedes. La contrasenya sutilitza per protegir les dades de la seva cartera al dispositiu Android. Utilitzeu una contrasenya sòlida - encara millor si utiliza una frase.</p>
<p>Introduïu el número de bloc de la primera transacció realitzada en aquesta adreça en el camp "Restaurar Alçada". També podeu utilitzar una data en el format AAAA-MM-DD. Si no esteu segurs, introduïu una data / alçada de bloc aproximada <em> abans </em> d'haver fer servir aquesta adreça del portamonedes.</p>
]]></string>
<string name="help_create_keys"><![CDATA[
<h1>Crear Portamonedes - Claus</h1>
<p>Si voleu recuperar el vostre portamonedes fent servir les vostres claus!</p>
<p>Introduïu un nom i contrasenya únics del portamonedes. La contrasenya sutilitza per protegir les dades de la seva cartera al dispositiu. Utilitzeu una contrasenya sòlida - encara millor si utiliza una frase.<p>
<p>Introduïu la vostra Adreça Monero en el camp \"Adreça Pública\" i ompliu \"Clau de Visualització\" i \"Clau de Despesa\" </p>
<p>Introduïu el número de bloc de la primera transacció realitzada en aquesta adreça en el
         camp "Restaurar Alçada". També podeu utilitzar una data en el format AAAA-MM-DD. Si no esteu segurs, introduïu una data / alçada de bloc aproximada <em> abans </em> d'haver fer servir aquesta adreça del portamonedes.</p>
]]></string>
<string name="help_create_view"><![CDATA[
<h1>Crear Portamonedes - Només de Lectura</h1>
<p>Si només voleu monitoritzar les transaccions entrants del portamonedes!</p>
<p>Introduïu un nom i contrasenya únics del portamonedes. La contrasenya sutilitza per protegir les dades de la seva cartera al dispositiu. Utilitzeu una contrasenya sòlida - encara millor si utiliza una frase.<p>
<p>Introduïu la vostra Adreça Monero en el camp \"Adreça Pública\" i ompliu \"Clau de Visualització\".</p>
<p>Introduïu el número de bloc de la primera transacció realitzada en aquesta adreça en el camp "Restaurar Alçada". També podeu utilitzar una data en el format AAAA-MM-DD. Si no esteu segurs, introduïu una data / alçada de bloc aproximada <em> abans </em> d'haver fer servir aquesta adreça del portamonedes.</p>
]]></string>
<string name="help_details"><![CDATA[
<h1>Detalls del portamonedes</h1>
<h2>Adreça pública</h2>
La vostra adreça pública és com el vostre número de compte bancari que podeu compartir amb qualsevol persona sense haver de tenir por de perdre el vostres Monero. La gent enviarà Monero al seu portamonedes mitjançant aquesta adreça.
<h2>Clau Mnemotècnica</h2>
Aquestes són les úniques dades necessàries per a recuperar el vostre portamonedes i obtenir accés total als vostres fons.
         Mantenir-la segura i privada és molt important ja que permet que <em> qualsevol persona</em> tingui accés total als vostres fons! Si no us heu escrit en cap lloc segur, feu-ho!
<h2>Contrasenya de Recuperació d'Arxius del Portamonedes</h2>
Assegureu-vos que heu escrit aquesta contrasenya. Si restabliu el dispositiu o desinstal·leu laplicació, la necessitarà per tornar a accedir al seu portamonedes.<br/>
<h3>CrAzYsenya</h3>
Si la contrasenya que es mostra aquí és de 52 caràcters alfanumèrics en grups de 4 - felicitats!
        Els fitxers del portamonedes estan protegits amb una clau de 256 bits generada per la funció de seguretat del vostre dispositiu basades en la contrasenya que heu triat (durant la creació o al canviar-la). Això ho fa extremadament difícil de piratejar!<br/>
Aquesta opció és obligatoria per a tots els nous portamonedes.
<h3>Contrasenya de Llegat</h3>
Si veieu la vostra contrasenya aquí, els fitxers del portamonedes no estan tan segurs com quan s'utilitza la CrAzYsenya. Per solucionar-ho, seleccioneu \"Canviar Contrasenya\" des del menú. Després d'entrar una nova contrasenya (potser fins i tot la mateixa que abans) laplicació generarà una CrAzYsenya per a protegir els vostres arxius del portamonedes. Anoteu-la!
<h3>CrAzYsenya del Portamonedes</h3>
Si mai necessiteu tornar a instal·lar Monerujo (per exemple, després de restablir el telèfon o canviar-lo per un de nou) o voleu utilitzar els arxius del portamonedes en un altre dispositiu o PC, cal que ho feu utilitzant aquesta Contrasenya de Recuperació (CrAzYsenya) per tornar a accedir al vostre portamonedes. <br/>
        En seleccionar \"Canviar Contrasenya\" des del menú, podeu triar una nova contrasenya. Aneu amb compte que això generarà una nova Contrasenya de Recuperació (CrAzYsenya). Anoteu-la!
<h2>Clau de Visualització</h2>
La vostra clau de visualització es pot utilitzar per monitoritzar les transaccions entrants al vostre portamonedes sense donar-ne permís per gastar els fons a dins seu.
<h2>Clau de Despesa</h2>
La vostra clau de despesa permet a qualsevol persona gastar els Monero associats al seu portamonedes, així que no ho compartiu amb ningú, mantingueu-la segura com la vostra Llavor Mnemotècnica.
]]></string>
<string name="help_list"><![CDATA[
<h1>Llista de Portamonedes</h1>
<h2>Node</h2>
<p>Monerujo utilitza un node remot per comunicar-se amb la xarxa Monero sense necessitat
         de descarregar i emmagatzemar una còpia de tota la blockchain sencera. Podeu trobar una llista dels nodes remots més populars o aprendre a configurar el vostre propi node remot aquí https://moneroworld.com/<p>
<p>Monerujo té alguns Nodes remots incorporats. Se'n recorda dels últims cinc nodes empleats.</p>
<h2>Portamonedes</h2>
<p>Aquí podeu veure els portamonedes. Es troben a la carpeta <tt> monerujo </tt>
         dins lemmagatzematge intern del dispositiu. Podeu utilitzar una aplicació dexploració d'arxius per veure'ls. Haurieu de fer còpies de seguretat daquesta carpeta de manera regular en un emmagatzematge extern al dispositiu en cas que el vostre dispositiu exploti o el robin.</p>
<p>Seleccioneu una portamonedes per obrir-lo o premeu el botó "+" per crear-ne un de nou.
         O seleccioneu una de les operacions del portamonedes:</p>
<h3>Detalls</h3>
<p>Mostra els detalls del portamonedes, la llavor i les seves claus.</p>
<h3>Rebre</h3>
<p>Crea un codi QR per rebre Moneroj.</p>
<h3>Canvi de Nom</h3>
<p>Canvia el nom del portamonedes. El canvi de nom no afecta a les còpies de seguretat.</p>
<h3>Còpia de Seguretat</h3>
<p>Feu una còpia del portamonedes en la carpeta <tt> backups </tt> dins del <tt> monerujo </tt>
         per a sobreescriure còpies anteriors.</p>
<h3>Arxiu</h3>
<p>Feu una còpia de seguretat i a continuació elimineu el portamonedes. La còpia es mantindrà a la carpeta <tt> backups </tt>. Si ja no necessiteu les vostres còpies de seguretat les haureu deliminar amb un explorador d'arxius o una aplicació segura.</p>
]]></string>
<string name="help_tx_details"><![CDATA[
<h1>Detalls de Transacció</h1>
<h2>Destí</h2>
Aquesta és l'adreça pública del portamonedes on esteu enviant els Monero.
<h2>ID de Pagament</h2>
Podeu utilitzar un ID de pagament per identificar el motiu pel qual heu enviat a Monero entre dues parts. Això és totalment opcional i privat. Per exemple, això pot permetre a una empresa associar la transacció amb un article que heu comprat.
<h2>ID de Transacció</h2>
Aquest és el vostre ID de transacció que podeu utilitzar per identificar la vostra transacció oculta en un Explorador de Blockchain de Monero com <a href="https:// xmrchain.net/"> https://xmrchain.net/ </a>
<h2>Clau de Transacció</h2>
Aquesta és la vostra clau privada de transacció, manteniu-la segureta ja que mostrar-la a tercers els revelia quina signatura dins un anell és la vostra, fent que la vostra transacció sigui transparent.
<h2>Bloc</h2>
Aquest és el bloc de la cadena de Monero on la vostra transacció ha estat inclosa.
]]></string>
<string name="help_send"><![CDATA[
<h1>Enviar</h1>
<h2>Adreça del Receptor</h2>
<p>Aquesta és l'adreça pública del portamonedes a la qual esteu enviant Moneroj. Podeu copiar-la al porta-retalls, escanejar un codi QR o introduir-la manualment. Assegureu- vos de confirmar-ho a consciencia i que no estigueu enviant monedes a la direcció incorrecta.</p>
<p>A més dutilitzar una adreça XMR, també podeu utilitzar
<ul>
<li>un OpenAlias per XMR o BTC</li>
<li>una adreça BTC</li>
<li>un bitcoin: URI (inclòs BIP70 com bitpay)</li>
</u>
Tingueu en compte que lenviament de BTC es duu a terme a través del servei XMR.TO (consulteu https://xmr.to per a més detalls). Consulteu la secció sobre lenviament de BTC més avall.</p>
<h2>ID de Pagament</h2>
<p>Podeu utilitzar un ID de pagament per identificar el motiu pel qual heu enviat a Monero entre dues parts. Això és totalment opcional i privat. Per exemple, això pot permetre a una empresa associar la transacció amb un article que heu comprat.<p>
<h1>Enviant BTC</h1>
<h2>XMR.TO</h2>
<p>XMR.TO és un servei de tercers que fa proporciona el canvi de Monero a Bitcoin.
         Nosaltres fem servir l'API XMR.TO per integrar els pagaments de Bitcoin a Monerujo. Si us plau, doneu un cop d'ull a https://xmr.to i decidiu vosaltres mateixos si és una cosa que volgueu utilitzar. L'equip de Monerujo no està associat amb XMR.TO i no pot oferir assistència amb el seu servei.</p>
<h2>Tipus de Canvi XMR.TO<h2>
<p>A la pantalla \"Quantitat\" es mostraran els paràmetres actuals del servei XMR.TO. Aquests
         inclouen el tipus de canvi actual, així com els límits de BTC superiors i inferiors. Tingueu en compte que la tarifa no està garantida en aquell mateix moment. També veureu
         la quantitat fins a la qual la transacció BTC sexecutarà instantàniament sense esperar
         a confirmacions de XMR (consulteu les preguntes freqüents sobre XMR.TO per obtenir més informació). Tingueu en compte que el servei XMR.TO no suposa càrrecs addicionals: oi que mola?</p>
<h2>Ordre de compra XMR.TO<h2>
<p>A la pantalla \"Confirmar\", veureu lordre de compra XMR.TO real. Aquesta comanda és vàlida per a
         un temps limitat: és possible que veieu un compte enrere en el botó \"Gastar\". El tipus de canvi pot
         ser diferent del mostrat en pantalles anteriors.</p>
<h2>Clau Secreta XMR.TO<h2>
<p>Com que Monerujo només gestiona la part Monero de la vostra transacció, la vostra clau secreta per XMR.TO es pot utilitzar per fer el seguiment de la part de Bitcoin de la vostra comanda a la pàgina principal de XMR.TO.</p>
<p>Tingueu en compte que aquesta clau secreta només és vàlida durant 24 hores després que la transacció
         ha començat!</p>
<h2>Compte enrere XMR.TO!</h2>
<p>Una vegada el compte enrere arribi a zero haureu dobtenir una nova sol·licitud de XMR.TO tornant enrere al pas anterior i després tornar a la pantalla de \"Confirmar\".</p>
]]></string>
<string name="help_xmrto"><![CDATA[
<h1>Enviant BTC</h1>
<h2>XMR.TO</h2>
<p>XMR.TO és un servei de tercers que fa proporciona el canvi de Monero a Bitcoin.
         Nosaltres fem servir l'API XMR.TO per integrar els pagaments de Bitcoin a Monerujo. Si us plau, doneu un cop d'ull a https://xmr.to i decidiu vosaltres mateixos si és una cosa que volgueu utilitzar. L'equip de Monerujo no està associat amb XMR.TO i no pot oferir assistència amb el seu servei.</p>
<h2>Tipus de Canvi XMR.TO<h2>
<p>A la pantalla \"Quantitat\" es mostraran els paràmetres actuals del servei XMR.TO. Aquests
         inclouen el tipus de canvi actual, així com els límits de BTC superiors i inferiors. Tingueu en compte que la tarifa no està garantida en aquell mateix moment. També veureu
         la quantitat fins a la qual la transacció BTC sexecutarà instantàniament sense esperar
         a confirmacions de XMR (consulteu les preguntes freqüents sobre XMR.TO per obtenir més informació). Tingueu en compte que el servei XMR.TO no suposa càrrecs addicionals: oi que mola?</p>
<h2>Ordre de compra XMR.TO<h2>
<p>A la pantalla \"Confirmar\", veureu lordre de compra XMR.TO real. Aquesta comanda és vàlida per a
         un temps limitat: és possible que veieu un compte enrere en el botó \"Gastar\". El tipus de canvi pot
         ser diferent del mostrat en pantalles anteriors.</p>
<h2>Clau Secreta XMR.TO<h2>
<p>Com que Monerujo només gestiona la part Monero de la vostra transacció, la vostra clau secreta per XMR.TO es pot utilitzar per fer el seguiment de la part de Bitcoin de la vostra comanda a la pàgina principal de XMR.TO.</p>
<p>Tingueu en compte que aquesta clau secreta només és vàlida durant 24 hores després que la transacció
         ha començat!</p>
<h2>Compte enrere XMR.TO!</h2>
<p>Una vegada el compte enrere arribi a zero haureu dobtenir una nova sol·licitud de XMR.TO tornant enrere al pas anterior i després tornar a la pantalla de \"Confirmar\".</p>
]]></string>
<string name="help_wallet"><![CDATA[
<h1>El Portamonedes</h1>
<h2>Mode de carrer</h2>
<p>El mode de carrer es pot habilitar / desactivar des del menú o a la icona principal de Gunther. En aquest mode, el vostre balanç no es mostra a cap pantalla perquè pugueu utilitzar la vostra cartera amb seguretat al carrer, al pub o qualsevol lloc públic. Les transaccions anteriors també estan ocultes. Es mostraran les noves transaccions perquè pogueu veure que heu enviat / rebut els vostres estimats Moneroj!</p>
<h2>Escanejant</h2>
Perquè a Monero li agrada mantenir les coses privades, cada cop que obri un portamonedes Monerujo hem d'escanejar la blockchain per veure si s'han enviat nous moneroj al seu portamonedes. Això només emmagatzema l'informació al telèfon que pertany al seu portamonedes. De vegades pot trigar una estona perquè no sha sincronitzat en molt de temps.
<h2>El Balanç</h2>
<p><b>Ajuda! El balanç del meu portamonedes ha desaparegut o consta com a no confirmat!</b><br/>
No patiu! Quan envieu fons desde el vostre portamonedes part del balanç apareixerà com a no confirmat de forma temporal.
Això succeeix pel fet de com Monero és intercanviat a través de la blockchain i com es produeix el canvi.
        Podeu llegir més sobre el canvi a https://getmonero.org/resources/moneropedia/change.html
<h2>Llista de Transacció</h2>
<p>Llistat de les transaccions del portamonedes. Els portamonedes de visualització només mostraran les transaccions entrants.</p>
]]></string>
<string name="help_node"><![CDATA[
<h1>Nodes</h1>
<h2>TL;DR</h2>
<p>Actualitzeu la llista de nodes prement cap avall; marqueu 3&#8211;5 nodes per permetre a Monerujo
         triar el millor per a tu!</p>
<h2>Què és un Node?</h2>
<p>Monerujo utilitza un Node Remot (de vegades també anomenat daemon) per comunicar-se
         la xarxa Monero sense haver de descarregar i emmagatzemar una còpia de
         tota la blockchain mateixa.<p>
<h2>Llista de Nodes</h2>
<p>Si la llista està buida, podeu afegir nous nodes manualment o deixar que Monerujo
         escanegi la xarxa per vostè. O ambdós. Llegiu &#8230;</p>
<p>La llista de nodes mostra tots els nodes coneguts. A més, la marca de temps
         de lúltim bloc conegut per a cada node es mostra sota el nom del node. La icona
         que representa el temps de resposta del node
         (que indica el nivell de connectivitat estimat)
         es mostra al costat de cada node.</p>
<p>Es pot marcar qualsevol node de la llista per utilitzar-lo més endevant.
         Es descartaràn els nodes que no hagin estat seleccionats.<p>
<p>Monerujo escollirà el node òptim (marcat) cada vegada que l'utilitzeu.
         Això ho fa mitjançant la comprovació de l'alçada de bloc (com d'actualitzat
         està el node?), així com el temps de resposta (què tan ràpidament respon el node a les peticions?).</p>
<p>La llista sordena per aquestes característiques, de manera que el node superior seria el que Monerujo
         triaria ara mateix. La part inferior de la llista mostraria els nodes més lents o no disponibles.</p>
<h2>Afegir Node</h2>
<p>Si premeu el botó &quot;Afegir Node&quot; a la part inferior, se us demanarà
        que introduïu els detalls del node al següent diàleg.
        El &quot;Adreça&quot; és el nom del servidor o adreça IP del node - aquesta és la única
        entrada obligatòria.
        Introduïu el &quot;Port&quot; si el node s'executa en un port no predeterminat (per exemple, 18089).
        També podeu anomenar opcionalment el node, de manera que el pugueu identificar més fàcilment més endavant.
        Alguns nodes necessiten credencials per utilitzar-los. Introduïu el nom dusuari i
        contrasenya proporcionats als camps corresponents. Ara podeu &quot;Test&quot; aquesta configuració.
        Els &quot;Resultats de les Proves&quot; mostraran l'alçada de bloc, el temps de resposta i l'IP real.
        El resultat també pot ser un error - generalment perquè el nom del servidor proporcionat no és
        accessible dins d'un temps raonable o les credencials són incorrectes.
        O la combinació de nom del servidor/port no apunta cap a un node real de Monero!
        Un cop aprovada la prova (sense error), ja estás llest per prémer &quot;D'acord&quot; per desar iamp;
        marcar aquest node.</p>
<h2>Escanejar Nodes</h2>
<p>A més, podeu escanejar la xarxa per buscar nodes. Monerujo començarà
         escanejant la xarxa per als nodes remots al port 18089. Començarà per preguntar als vostres
         nodes marcats per altres companys de la xarxa P2P de Monero, i després continuarà
         preguntant-los per als seus companys, etc. Si no teniu cap node marcat
         (o no ens informen sobre els seus companys),
         Monerujo anirà directament als nodes de llavor de Monero codificats a dins de Monero.
         Lescaneig satura quan troba un total de 10 nodes remots.</p>
]]></string>
<string name="help_uri"><![CDATA[
<h1>Fer servir un enllaç de pagament</h1>
<p>Heu iniciat monerujo amb un enllaç de pagament. Per enviar fons, feu el següent:</p>
<p>
1. Obrir el portamonedes desde la qual voleu gastar els fons<br>
2. Espereu fins que es sincronitzi el portamonedes i el botó de "Donar" apareixi<br>
3. Premeu el botó de "Donar"
</p>
<p>Els detalls de pagament es completaran. Comproveu-les i continueu com a qualsevol altra transacció.</p>
]]></string>
<string name="help_ok">Ja ho tinc!</string> <!-- Note: "Got it" as in "I understand this" -->
</resources>

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More