Node-o-matiC

merge-requests/3/head
wow nero 6 years ago
parent 2dad55e498
commit 973472e0ef
No known key found for this signature in database
GPG Key ID: 4386E69AF260078D

4
.gitignore vendored

@ -7,3 +7,7 @@
.externalNativeBuild
.DS_Store
/app/release
/app/alphaMainnet
/app/prodMainnet
/app/alphaStagenet
/app/prodStagenet

@ -1,14 +1,14 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 27
versionCode 140
versionName "1.9.0 'We Comin' Rougher'"
targetSdkVersion 28
versionCode 153
versionName "1.10.3 'Node-O-matiC'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
@ -19,13 +19,22 @@ android {
}
}
flavorDimensions "version"
flavorDimensions 'type', 'net'
productFlavors {
mainnet {
dimension 'net'
}
stagenet {
dimension 'net'
applicationIdSuffix '.stage'
}
alpha {
applicationIdSuffix ".alpha"
versionNameSuffix " (alpha)"
dimension 'type'
applicationIdSuffix '.alpha'
versionNameSuffix ' (alpha)'
}
prod {
dimension 'type'
}
}
@ -91,9 +100,11 @@ dependencies {
implementation "com.android.support:support-v4:$rootProject.ext.supportVersion"
implementation "com.android.support:recyclerview-v7:$rootProject.ext.supportVersion"
implementation "com.android.support:cardview-v7:$rootProject.ext.supportVersion"
implementation "com.android.support:swiperefreshlayout:$rootProject.ext.supportVersion"
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
implementation "com.burgstaller:okhttp-digest:1.18"
implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
implementation 'com.nulab-inc:zxcvbn:1.2.3'

@ -16,7 +16,8 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/MyMaterialTheme">
android:theme="@style/MyMaterialTheme"
android:usesCleartextTraffic="true">
<activity
android:name=".WalletActivity"
@ -43,7 +44,7 @@
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity>
<service
android:name=".service.WalletService"
android:description="@string/service_description"

@ -23,9 +23,9 @@ package com.btchip.comm;
import com.btchip.BTChipException;
public interface BTChipTransport {
public byte[] exchange(byte[] command);
byte[] exchange(byte[] command);
public void close();
void close();
public void setDebug(boolean debugFlag);
void setDebug(boolean debugFlag);
}

@ -0,0 +1,145 @@
/*
* 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.levin.data;
import com.m2049r.levin.util.HexHelper;
import com.m2049r.levin.util.LevinReader;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Bucket {
// constants copied from monero p2p & epee
public final static int P2P_COMMANDS_POOL_BASE = 1000;
public final static int COMMAND_HANDSHAKE_ID = P2P_COMMANDS_POOL_BASE + 1;
public final static int COMMAND_TIMED_SYNC_ID = P2P_COMMANDS_POOL_BASE + 2;
public final static int COMMAND_PING_ID = P2P_COMMANDS_POOL_BASE + 3;
public final static int COMMAND_REQUEST_STAT_INFO_ID = P2P_COMMANDS_POOL_BASE + 4;
public final static int COMMAND_REQUEST_NETWORK_STATE_ID = P2P_COMMANDS_POOL_BASE + 5;
public final static int COMMAND_REQUEST_PEER_ID_ID = P2P_COMMANDS_POOL_BASE + 6;
public final static int COMMAND_REQUEST_SUPPORT_FLAGS_ID = P2P_COMMANDS_POOL_BASE + 7;
public final static long LEVIN_SIGNATURE = 0x0101010101012101L; // Bender's nightmare
public final static long LEVIN_DEFAULT_MAX_PACKET_SIZE = 100000000; // 100MB by default
public final static int LEVIN_PACKET_REQUEST = 0x00000001;
public final static int LEVIN_PACKET_RESPONSE = 0x00000002;
public final static int LEVIN_PROTOCOL_VER_0 = 0;
public final static int LEVIN_PROTOCOL_VER_1 = 1;
public final static int LEVIN_OK = 0;
public final static int LEVIN_ERROR_CONNECTION = -1;
public final static int LEVIN_ERROR_CONNECTION_NOT_FOUND = -2;
public final static int LEVIN_ERROR_CONNECTION_DESTROYED = -3;
public final static int LEVIN_ERROR_CONNECTION_TIMEDOUT = -4;
public final static int LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL = -5;
public final static int LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED = -6;
public final static int LEVIN_ERROR_FORMAT = -7;
public final static int P2P_SUPPORT_FLAG_FLUFFY_BLOCKS = 0x01;
public final static int P2P_SUPPORT_FLAGS = P2P_SUPPORT_FLAG_FLUFFY_BLOCKS;
final private long signature;
final private long cb;
final public boolean haveToReturnData;
final public int command;
final public int returnCode;
final private int flags;
final private int protcolVersion;
final byte[] payload;
final public Section payloadSection;
// create a request
public Bucket(int command, byte[] payload) throws IOException {
this.signature = LEVIN_SIGNATURE;
this.cb = payload.length;
this.haveToReturnData = true;
this.command = command;
this.returnCode = 0;
this.flags = LEVIN_PACKET_REQUEST;
this.protcolVersion = LEVIN_PROTOCOL_VER_1;
this.payload = payload;
payloadSection = LevinReader.readPayload(payload);
}
// create a response
public Bucket(int command, byte[] payload, int rc) throws IOException {
this.signature = LEVIN_SIGNATURE;
this.cb = payload.length;
this.haveToReturnData = false;
this.command = command;
this.returnCode = rc;
this.flags = LEVIN_PACKET_RESPONSE;
this.protcolVersion = LEVIN_PROTOCOL_VER_1;
this.payload = payload;
payloadSection = LevinReader.readPayload(payload);
}
public Bucket(DataInput in) throws IOException {
signature = in.readLong();
cb = in.readLong();
haveToReturnData = in.readBoolean();
command = in.readInt();
returnCode = in.readInt();
flags = in.readInt();
protcolVersion = in.readInt();
if (signature == Bucket.LEVIN_SIGNATURE) {
if (cb > Integer.MAX_VALUE)
throw new IllegalArgumentException();
payload = new byte[(int) cb];
in.readFully(payload);
} else
throw new IllegalStateException();
payloadSection = LevinReader.readPayload(payload);
}
public Section getPayloadSection() {
return payloadSection;
}
public void send(DataOutput out) throws IOException {
out.writeLong(signature);
out.writeLong(cb);
out.writeBoolean(haveToReturnData);
out.writeInt(command);
out.writeInt(returnCode);
out.writeInt(flags);
out.writeInt(protcolVersion);
out.write(payload);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("sig: ").append(signature).append("\n");
sb.append("cb: ").append(cb).append("\n");
sb.append("call: ").append(haveToReturnData).append("\n");
sb.append("cmd: ").append(command).append("\n");
sb.append("rc: ").append(returnCode).append("\n");
sb.append("flags:").append(flags).append("\n");
sb.append("proto:").append(protcolVersion).append("\n");
sb.append(HexHelper.bytesToHex(payload)).append("\n");
sb.append(payloadSection.toString());
return sb.toString();
}
}

@ -0,0 +1,125 @@
/*
* 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.levin.data;
import com.m2049r.levin.util.HexHelper;
import com.m2049r.levin.util.LevinReader;
import com.m2049r.levin.util.LevinWriter;
import com.m2049r.levin.util.LittleEndianDataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Section {
// constants copied from monero p2p & epee
static final public int PORTABLE_STORAGE_SIGNATUREA = 0x01011101;
static final public int PORTABLE_STORAGE_SIGNATUREB = 0x01020101;
static final public byte PORTABLE_STORAGE_FORMAT_VER = 1;
static final public byte PORTABLE_RAW_SIZE_MARK_MASK = 0x03;
static final public byte PORTABLE_RAW_SIZE_MARK_BYTE = 0;
static final public byte PORTABLE_RAW_SIZE_MARK_WORD = 1;
static final public byte PORTABLE_RAW_SIZE_MARK_DWORD = 2;
static final public byte PORTABLE_RAW_SIZE_MARK_INT64 = 3;
static final long MAX_STRING_LEN_POSSIBLE = 2000000000; // do not let string be so big
// data types
static final public byte SERIALIZE_TYPE_INT64 = 1;
static final public byte SERIALIZE_TYPE_INT32 = 2;
static final public byte SERIALIZE_TYPE_INT16 = 3;
static final public byte SERIALIZE_TYPE_INT8 = 4;
static final public byte SERIALIZE_TYPE_UINT64 = 5;
static final public byte SERIALIZE_TYPE_UINT32 = 6;
static final public byte SERIALIZE_TYPE_UINT16 = 7;
static final public byte SERIALIZE_TYPE_UINT8 = 8;
static final public byte SERIALIZE_TYPE_DUOBLE = 9;
static final public byte SERIALIZE_TYPE_STRING = 10;
static final public byte SERIALIZE_TYPE_BOOL = 11;
static final public byte SERIALIZE_TYPE_OBJECT = 12;
static final public byte SERIALIZE_TYPE_ARRAY = 13;
static final public byte SERIALIZE_FLAG_ARRAY = (byte) 0x80;
private final Map<String, Object> entries = new HashMap<String, Object>();
public void add(String key, Object entry) {
entries.put(key, entry);
}
public int size() {
return entries.size();
}
public Set<Map.Entry<String, Object>> entrySet() {
return entries.entrySet();
}
public Object get(String key) {
return entries.get(key);
}
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("\n");
for (Map.Entry<String, Object> entry : entries.entrySet()) {
sb.append(entry.getKey()).append("=");
final Object value = entry.getValue();
if (value instanceof List) {
@SuppressWarnings("unchecked") final List<Object> list = (List<Object>) value;
for (Object listEntry : list) {
sb.append(listEntry.toString()).append("\n");
}
} else if (value instanceof String) {
sb.append("(").append(value).append(")\n");
} else if (value instanceof byte[]) {
sb.append(HexHelper.bytesToHex((byte[]) value)).append("\n");
} else {
sb.append(value.toString()).append("\n");
}
}
return sb.toString();
}
static public Section fromByteArray(byte[] buffer) {
try {
return LevinReader.readPayload(buffer);
} catch (IOException ex) {
throw new IllegalStateException();
}
}
public byte[] asByteArray() {
try {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
DataOutput out = new LittleEndianDataOutputStream(bas);
LevinWriter writer = new LevinWriter(out);
writer.writePayload(this);
return bas.toByteArray();
} catch (IOException ex) {
throw new IllegalStateException();
}
}
}

@ -0,0 +1,197 @@
/*
* 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.levin.scanner;
import com.m2049r.xmrwallet.data.NodeInfo;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
public class Dispatcher implements PeerRetriever.OnGetPeers {
static final public int NUM_THREADS = 50;
static final public int MAX_PEERS = 1000;
static final public long MAX_TIME = 30000000000L; //30 seconds
private int peerCount = 0;
final private Set<NodeInfo> knownNodes = new HashSet<>(); // set of nodes to test
final private Set<NodeInfo> rpcNodes = new HashSet<>(); // set of RPC nodes we like
final private ExecutorService exeService = Executors.newFixedThreadPool(NUM_THREADS);
public interface Listener {
void onGet(NodeInfo nodeInfo);
}
private Listener listener;
public Dispatcher(Listener listener) {
this.listener = listener;
}
public Set<NodeInfo> getRpcNodes() {
return rpcNodes;
}
public int getPeerCount() {
return peerCount;
}
public boolean getMorePeers() {
return peerCount < MAX_PEERS;
}
public void awaitTermination(int nodesToFind) {
try {
final long t = System.nanoTime();
while (!jobs.isEmpty()) {
try {
Timber.d("Remaining jobs %d", jobs.size());
final PeerRetriever retrievedPeer = jobs.poll().get();
if (retrievedPeer.isGood() && getMorePeers())
retrievePeers(retrievedPeer);
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
Timber.d("Retrieved %s", nodeInfo);
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
nodeInfo.setName();
rpcNodes.add(nodeInfo);
Timber.d("RPC: %s", nodeInfo);
// the following is not totally correct but it works (otherwise we need to
// load much more before filtering - but we don't have time
if (listener != null) listener.onGet(nodeInfo);
if (rpcNodes.size() >= nodesToFind) {
Timber.d("are we done here?");
filterRpcNodes();
if (rpcNodes.size() >= nodesToFind) {
Timber.d("we're done here");
break;
}
}
}
if (System.nanoTime() - t > MAX_TIME) break; // watchdog
} catch (ExecutionException ex) {
Timber.d(ex); // tell us about it and continue
}
}
} catch (InterruptedException ex) {
Timber.d(ex);
} finally {
Timber.d("Shutting down!");
exeService.shutdownNow();
try {
exeService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException ex) {
Timber.d(ex);
}
}
filterRpcNodes();
}
static final public int HEIGHT_WINDOW = 1;
private boolean testHeight(long height, long consensus) {
return (height >= (consensus - HEIGHT_WINDOW))
&& (height <= (consensus + HEIGHT_WINDOW));
}
private long calcConsensusHeight() {
Timber.d("Calc Consensus height from %d nodes", rpcNodes.size());
final Map<Long, Integer> nodeHeights = new TreeMap<Long, Integer>();
for (NodeInfo info : rpcNodes) {
if (!info.isValid()) continue;
Integer h = nodeHeights.get(info.getHeight());
if (h == null)
h = 0;
nodeHeights.put(info.getHeight(), h + 1);
}
long consensusHeight = 0;
long consensusCount = 0;
for (Map.Entry<Long, Integer> entry : nodeHeights.entrySet()) {
final long entryHeight = entry.getKey();
int count = 0;
for (long i = entryHeight - HEIGHT_WINDOW; i <= entryHeight + HEIGHT_WINDOW; i++) {
Integer v = nodeHeights.get(i);
if (v == null)
v = 0;
count += v;
}
if (count >= consensusCount) {
consensusCount = count;
consensusHeight = entryHeight;
}
Timber.d("%d - %d/%d", entryHeight, count, entry.getValue());
}
return consensusHeight;
}
private void filterRpcNodes() {
long consensus = calcConsensusHeight();
Timber.d("Consensus Height = %d for %d nodes", consensus, rpcNodes.size());
for (Iterator<NodeInfo> iter = rpcNodes.iterator(); iter.hasNext(); ) {
NodeInfo info = iter.next();
// don't remove favourites
if (!info.isFavourite()) {
if (!testHeight(info.getHeight(), consensus)) {
iter.remove();
Timber.d("Removed %s", info);
}
}
}
}
// TODO: does this NEED to be a ConcurrentLinkedDeque?
private ConcurrentLinkedDeque<Future<PeerRetriever>> jobs = new ConcurrentLinkedDeque<>();
private void retrievePeer(NodeInfo nodeInfo) {
if (knownNodes.add(nodeInfo)) {
Timber.d("\t%d:%s", knownNodes.size(), nodeInfo);
jobs.add(exeService.submit(new PeerRetriever(nodeInfo, this)));
peerCount++; // jobs.size() does not perform well
}
}
private void retrievePeers(PeerRetriever peer) {
for (InetSocketAddress socketAddress : peer.getPeers()) {
if (getMorePeers())
retrievePeer(new NodeInfo(socketAddress));
else
break;
}
}
public void seedPeers(Collection<NodeInfo> seedNodes) {
for (NodeInfo node : seedNodes) {
node.clear();
if (node.isFavourite()) {
rpcNodes.add(node);
if (listener != null) listener.onGet(node);
}
retrievePeer(node);
}
}
}

@ -0,0 +1,226 @@
/*
* 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.levin.scanner;
import com.m2049r.levin.data.Bucket;
import com.m2049r.levin.data.Section;
import com.m2049r.levin.util.HexHelper;
import com.m2049r.levin.util.LittleEndianDataInputStream;
import com.m2049r.levin.util.LittleEndianDataOutputStream;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.util.Helper;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import timber.log.Timber;
public class PeerRetriever implements Callable<PeerRetriever> {
static final public int CONNECT_TIMEOUT = 500; //ms
static final public int SOCKET_TIMEOUT = 500; //ms
static final public long PEER_ID = new Random().nextLong();
static final private byte[] HANDSHAKE = handshakeRequest().asByteArray();
static final private byte[] FLAGS_RESP = flagsResponse().asByteArray();
final private List<InetSocketAddress> peers = new ArrayList<>();
private NodeInfo nodeInfo;
private OnGetPeers onGetPeersCallback;
public interface OnGetPeers {
boolean getMorePeers();
}
public PeerRetriever(NodeInfo nodeInfo, OnGetPeers onGetPeers) {
this.nodeInfo = nodeInfo;
this.onGetPeersCallback = onGetPeers;
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
public boolean isGood() {
return !peers.isEmpty();
}
public List<InetSocketAddress> getPeers() {
return peers;
}
public PeerRetriever call() {
if (isGood()) // we have already been called?
throw new IllegalStateException();
// first check for an rpc service
nodeInfo.findRpcService();
if (onGetPeersCallback.getMorePeers())
try {
Timber.d("%s CONN", nodeInfo.getLevinSocketAddress());
if (!connect())
return this;
Bucket handshakeBucket = new Bucket(Bucket.COMMAND_HANDSHAKE_ID, HANDSHAKE);
handshakeBucket.send(getDataOutput());
while (true) {// wait for response (which may never come)
Bucket recv = new Bucket(getDataInput()); // times out after SOCKET_TIMEOUT
if ((recv.command == Bucket.COMMAND_HANDSHAKE_ID)
&& (!recv.haveToReturnData)) {
readAddressList(recv.payloadSection);
return this;
} else if ((recv.command == Bucket.COMMAND_REQUEST_SUPPORT_FLAGS_ID)
&& (recv.haveToReturnData)) {
Bucket flagsBucket = new Bucket(Bucket.COMMAND_REQUEST_SUPPORT_FLAGS_ID, FLAGS_RESP, 1);
flagsBucket.send(getDataOutput());
} else {// and ignore others
Timber.d("Ignored LEVIN COMMAND %d", recv.command);
}
}
} catch (IOException ex) {
} finally {
disconnect(); // we have what we want - byebye
Timber.d("%s DISCONN", nodeInfo.getLevinSocketAddress());
}
return this;
}
private void readAddressList(Section section) {
@SuppressWarnings("unchecked")
List<Section> peerList = (List<Section>) section.get("local_peerlist_new");
if (peerList != null) {
for (Section peer : peerList) {
Section adr = (Section) peer.get("adr");
Byte type = (Byte) adr.get("type");
if ((type == null) || (type != 1))
continue;
Section addr = (Section) adr.get("addr");
if (addr == null)
continue;
Integer ip = (Integer) addr.get("m_ip");
if (ip == null)
continue;
Short sport = (Short) addr.get("m_port");
if (sport == null)
continue;
int port = sport;
if (port < 0) // port is unsigned
port = port + 0x10000;
InetAddress inet = HexHelper.toInetAddress(ip);
// make sure this is an address we want to talk to (i.e. a remote address)
if (!inet.isSiteLocalAddress() && !inet.isAnyLocalAddress()
&& !inet.isLoopbackAddress()
&& !inet.isMulticastAddress()
&& !inet.isLinkLocalAddress()) {
peers.add(new InetSocketAddress(inet, port));
}
}
}
}
private Socket socket = null;
private boolean connect() {
if (socket != null) throw new IllegalStateException();
try {
socket = new Socket();
socket.connect(nodeInfo.getLevinSocketAddress(), CONNECT_TIMEOUT);
socket.setSoTimeout(SOCKET_TIMEOUT);
} catch (IOException ex) {
//Timber.d(ex);
return false;
}
return true;
}
private boolean isConnected() {
return socket.isConnected();
}
private void disconnect() {
try {
dataInput = null;
dataOutput = null;
if ((socket != null) && (!socket.isClosed())) {
socket.close();
}
} catch (IOException ex) {
Timber.d(ex);
} finally {
socket = null;
}
}
private DataOutput dataOutput = null;
private DataOutput getDataOutput() throws IOException {
if (dataOutput == null)
synchronized (this) {
if (dataOutput == null)
dataOutput = new LittleEndianDataOutputStream(
socket.getOutputStream());
}
return dataOutput;
}
private DataInput dataInput = null;
private DataInput getDataInput() throws IOException {
if (dataInput == null)
synchronized (this) {
if (dataInput == null)
dataInput = new LittleEndianDataInputStream(
socket.getInputStream());
}
return dataInput;
}
static private Section handshakeRequest() {
Section section = new Section(); // root object
Section nodeData = new Section();
nodeData.add("local_time", (new Date()).getTime());
nodeData.add("my_port", 0);
byte[] networkId = Helper.hexToBytes("1230f171610441611731008216a1a110"); // mainnet
nodeData.add("network_id", networkId);
nodeData.add("peer_id", PEER_ID);
section.add("node_data", nodeData);
Section payloadData = new Section();
payloadData.add("cumulative_difficulty", 1L);
payloadData.add("current_height", 1L);
byte[] genesisHash =
Helper.hexToBytes("418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3");
payloadData.add("top_id", genesisHash);
payloadData.add("top_version", (byte) 1);
section.add("payload_data", payloadData);
return section;
}
static private Section flagsResponse() {
Section section = new Section(); // root object
section.add("support_flags", Bucket.P2P_SUPPORT_FLAGS);
return section;
}
}

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.levin.util;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class HexHelper {
static public String bytesToHex(byte[] data) {
if ((data != null) && (data.length > 0))
return String.format("%0" + (data.length * 2) + "X",
new BigInteger(1, data));
else
return "";
}
static public InetAddress toInetAddress(int ip) {
try {
String ipAddress = String.format("%d.%d.%d.%d", (ip & 0xff),
(ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff));
return InetAddress.getByName(ipAddress);
} catch (UnknownHostException ex) {
throw new IllegalArgumentException(ex);
}
}
}

@ -0,0 +1,182 @@
/*
* 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.levin.util;
import com.m2049r.levin.data.Section;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
// Full Levin reader as seen on epee
public class LevinReader {
private DataInput in;
private LevinReader(byte[] buffer) {
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
in = new LittleEndianDataInputStream(bis);
}
static public Section readPayload(byte[] payload) throws IOException {
LevinReader r = new LevinReader(payload);
return r.readPayload();
}
private Section readPayload() throws IOException {
if (in.readInt() != Section.PORTABLE_STORAGE_SIGNATUREA)
throw new IllegalStateException();
if (in.readInt() != Section.PORTABLE_STORAGE_SIGNATUREB)
throw new IllegalStateException();
if (in.readByte() != Section.PORTABLE_STORAGE_FORMAT_VER)
throw new IllegalStateException();
return readSection();
}
private Section readSection() throws IOException {
Section section = new Section();
long count = readVarint();
while (count-- > 0) {
// read section name string
String sectionName = readSectionName();
section.add(sectionName, loadStorageEntry());
}
return section;
}
private Object loadStorageArrayEntry(int type) throws IOException {
type &= ~Section.SERIALIZE_FLAG_ARRAY;
return readArrayEntry(type);
}
private List<Object> readArrayEntry(int type) throws IOException {
List<Object> list = new ArrayList<Object>();
long size = readVarint();
while (size-- > 0)
list.add(read(type));
return list;
}
private Object read(int type) throws IOException {
switch (type) {
case Section.SERIALIZE_TYPE_UINT64:
case Section.SERIALIZE_TYPE_INT64:
return in.readLong();
case Section.SERIALIZE_TYPE_UINT32:
case Section.SERIALIZE_TYPE_INT32:
return in.readInt();
case Section.SERIALIZE_TYPE_UINT16:
case Section.SERIALIZE_TYPE_INT16:
return in.readShort();
case Section.SERIALIZE_TYPE_UINT8:
case Section.SERIALIZE_TYPE_INT8:
return in.readByte();
case Section.SERIALIZE_TYPE_OBJECT:
return readSection();
case Section.SERIALIZE_TYPE_STRING:
return readByteArray();
default:
throw new IllegalArgumentException("type " + type
+ " not supported");
}
}
private Object loadStorageEntry() throws IOException {
int type = in.readUnsignedByte();
if ((type & Section.SERIALIZE_FLAG_ARRAY) != 0)
return loadStorageArrayEntry(type);
if (type == Section.SERIALIZE_TYPE_ARRAY)
return readStorageEntryArrayEntry();
else
return readStorageEntry(type);
}
private Object readStorageEntry(int type) throws IOException {
return read(type);
}
private Object readStorageEntryArrayEntry() throws IOException {
int type = in.readUnsignedByte();
if ((type & Section.SERIALIZE_FLAG_ARRAY) != 0)
throw new IllegalStateException("wrong type sequences");
return loadStorageArrayEntry(type);
}
private String readSectionName() throws IOException {
int nameLen = in.readUnsignedByte();
return readString(nameLen);
}
private byte[] read(long count) throws IOException {
if (count > Integer.MAX_VALUE)
throw new IllegalArgumentException();
int len = (int) count;
final byte buffer[] = new byte[len];
in.readFully(buffer);
return buffer;
}
private String readString(long count) throws IOException {
return new String(read(count), StandardCharsets.US_ASCII);
}
private byte[] readByteArray(long count) throws IOException {
return read(count);
}
private byte[] readByteArray() throws IOException {
long len = readVarint();
return readByteArray(len);
}
private long readVarint() throws IOException {
long v = 0;
int b = in.readUnsignedByte();
int sizeMask = b & Section.PORTABLE_RAW_SIZE_MARK_MASK;
switch (sizeMask) {
case Section.PORTABLE_RAW_SIZE_MARK_BYTE:
v = b >>> 2;
break;
case Section.PORTABLE_RAW_SIZE_MARK_WORD:
v = readRest(b, 1) >>> 2;
break;
case Section.PORTABLE_RAW_SIZE_MARK_DWORD:
v = readRest(b, 3) >>> 2;
break;
case Section.PORTABLE_RAW_SIZE_MARK_INT64:
v = readRest(b, 7) >>> 2;
break;
default:
throw new IllegalStateException();
}
return v;
}
// this should be in LittleEndianDataInputStream because it has little
// endian logic
private long readRest(int firstByte, int bytes) throws IOException {
long result = firstByte;
for (int i = 0; i < bytes; i++) {
result = result + (in.readUnsignedByte() << 8);
}
return result;
}
}

@ -0,0 +1,98 @@
/*
* 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.levin.util;
import com.m2049r.levin.data.Section;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
// a simplified Levin Writer WITHOUT support for arrays
public class LevinWriter {
private DataOutput out;
public LevinWriter(DataOutput out) {
this.out = out;
}
public void writePayload(Section section) throws IOException {
out.writeInt(Section.PORTABLE_STORAGE_SIGNATUREA);
out.writeInt(Section.PORTABLE_STORAGE_SIGNATUREB);
out.writeByte(Section.PORTABLE_STORAGE_FORMAT_VER);
putSection(section);
}
private void writeSection(Section section) throws IOException {
out.writeByte(Section.SERIALIZE_TYPE_OBJECT);
putSection(section);
}
private void putSection(Section section) throws IOException {
writeVarint(section.size());
for (Map.Entry<String, Object> kv : section.entrySet()) {
byte[] key = kv.getKey().getBytes(StandardCharsets.US_ASCII);
out.writeByte(key.length);
out.write(key);
write(kv.getValue());
}
}
private void writeVarint(long i) throws IOException {
if (i <= 63) {
out.writeByte(((int) i << 2) | Section.PORTABLE_RAW_SIZE_MARK_BYTE);
} else if (i <= 16383) {
out.writeShort(((int) i << 2) | Section.PORTABLE_RAW_SIZE_MARK_WORD);
} else if (i <= 1073741823) {
out.writeInt(((int) i << 2) | Section.PORTABLE_RAW_SIZE_MARK_DWORD);
} else {
if (i > 4611686018427387903L)
throw new IllegalArgumentException();
out.writeLong((i << 2) | Section.PORTABLE_RAW_SIZE_MARK_INT64);
}
}
private void write(Object object) throws IOException {
if (object instanceof byte[]) {
byte[] value = (byte[]) object;
out.writeByte(Section.SERIALIZE_TYPE_STRING);
writeVarint(value.length);
out.write(value);
} else if (object instanceof String) {
byte[] value = ((String) object)
.getBytes(StandardCharsets.US_ASCII);
out.writeByte(Section.SERIALIZE_TYPE_STRING);
writeVarint(value.length);
out.write(value);
} else if (object instanceof Integer) {
out.writeByte(Section.SERIALIZE_TYPE_UINT32);
out.writeInt((int) object);
} else if (object instanceof Long) {
out.writeByte(Section.SERIALIZE_TYPE_UINT64);
out.writeLong((long) object);
} else if (object instanceof Byte) {
out.writeByte(Section.SERIALIZE_TYPE_UINT8);
out.writeByte((byte) object);
} else if (object instanceof Section) {
writeSection((Section) object);
} else {
throw new IllegalArgumentException();
}
}
}

@ -0,0 +1,564 @@
/*
* 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.levin.util;
import java.io.DataInput;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UTFDataFormatException;
/**
* A little endian java.io.DataInputStream (without readLine())
*/
public class LittleEndianDataInputStream extends FilterInputStream implements
DataInput {
/**
* Creates a DataInputStream that uses the specified underlying InputStream.
*
* @param in the specified input stream
*/
public LittleEndianDataInputStream(InputStream in) {
super(in);
}
@Deprecated
public final String readLine() {
throw new UnsupportedOperationException();
}
/**
* Reads some number of bytes from the contained input stream and stores
* them into the buffer array <code>b</code>. The number of bytes actually
* read is returned as an integer. This method blocks until input data is
* available, end of file is detected, or an exception is thrown.
*
* <p>
* If <code>b</code> is null, a <code>NullPointerException</code> is thrown.
* If the length of <code>b</code> is zero, then no bytes are read and
* <code>0</code> is returned; otherwise, there is an attempt to read at
* least one byte. If no byte is available because the stream is at end of
* file, the value <code>-1</code> is returned; otherwise, at least one byte
* is read and stored into <code>b</code>.
*
* <p>
* The first byte read is stored into element <code>b[0]</code>, the next
* one into <code>b[1]</code>, and so on. The number of bytes read is, at
* most, equal to the length of <code>b</code>. Let <code>k</code> be the
* number of bytes actually read; these bytes will be stored in elements
* <code>b[0]</code> through <code>b[k-1]</code>, leaving elements
* <code>b[k]</code> through <code>b[b.length-1]</code> unaffected.
*
* <p>
* The <code>read(b)</code> method has the same effect as: <blockquote>
*
* <pre>
* read(b, 0, b.length)
* </pre>
*
* </blockquote>
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of the
* stream has been reached.
* @throws IOException if the first byte cannot be read for any reason other than
* end of file, the stream has been closed and the underlying
* input stream does not support reading after close, or
* another I/O error occurs.
* @see FilterInputStream#in
* @see InputStream#read(byte[], int, int)
*/
public final int read(byte b[]) throws IOException {
return in.read(b, 0, b.length);
}
/**
* Reads up to <code>len</code> bytes of data from the contained input
* stream into an array of bytes. An attempt is made to read as many as
* <code>len</code> bytes, but a smaller number may be read, possibly zero.
* The number of bytes actually read is returned as an integer.
*
* <p>
* This method blocks until input data is available, end of file is
* detected, or an exception is thrown.
*
* <p>
* If <code>len</code> is zero, then no bytes are read and <code>0</code> is
* returned; otherwise, there is an attempt to read at least one byte. If no
* byte is available because the stream is at end of file, the value
* <code>-1</code> is returned; otherwise, at least one byte is read and
* stored into <code>b</code>.
*
* <p>
* The first byte read is stored into element <code>b[off]</code>, the next
* one into <code>b[off+1]</code>, and so on. The number of bytes read is,
* at most, equal to <code>len</code>. Let <i>k</i> be the number of bytes
* actually read; these bytes will be stored in elements <code>b[off]</code>
* through <code>b[off+</code><i>k</i><code>-1]</code>, leaving elements
* <code>b[off+</code><i>k</i><code>]</code> through
* <code>b[off+len-1]</code> unaffected.
*
* <p>
* In every case, elements <code>b[0]</code> through <code>b[off]</code> and
* elements <code>b[off+len]</code> through <code>b[b.length-1]</code> are
* unaffected.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of the
* stream has been reached.
* @throws NullPointerException If <code>b</code> is <code>null</code>.
* @throws IndexOutOfBoundsException If <code>off</code> is negative, <code>len</code> is
* negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @throws IOException if the first byte cannot be read for any reason other than
* end of file, the stream has been closed and the underlying
* input stream does not support reading after close, or
* another I/O error occurs.
* @see FilterInputStream#in
* @see InputStream#read(byte[], int, int)
*/
public final int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
/**
* See the general contract of the <code>readFully</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @param b the buffer into which the data is read.
* @throws EOFException if this input stream reaches the end before reading all
* the bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
/**
* See the general contract of the <code>readFully</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @param b the buffer into which the data is read.
* @param off the start offset of the data.
* @param len the number of bytes to read.
* @throws EOFException if this input stream reaches the end before reading all
* the bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final void readFully(byte b[], int off, int len) throws IOException {
if (len < 0)
throw new IndexOutOfBoundsException();
int n = 0;
while (n < len) {
int count = in.read(b, off + n, len - n);
if (count < 0)
throw new EOFException();
n += count;
}
}
/**
* See the general contract of the <code>skipBytes</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @throws IOException if the contained input stream does not support seek, or
* the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
*/
public final int skipBytes(int n) throws IOException {
int total = 0;
int cur = 0;
while ((total < n) && ((cur = (int) in.skip(n - total)) > 0)) {
total += cur;
}
return total;
}
/**
* See the general contract of the <code>readBoolean</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the <code>boolean</code> value read.
* @throws EOFException if this input stream has reached the end.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final boolean readBoolean() throws IOException {
int ch = in.read();
if (ch < 0)
throw new EOFException();
return (ch != 0);
}
/**
* See the general contract of the <code>readByte</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next byte of this input stream as a signed 8-bit
* <code>byte</code>.
* @throws EOFException if this input stream has reached the end.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final byte readByte() throws IOException {
int ch = in.read();
if (ch < 0)
throw new EOFException();
return (byte) (ch);
}
/**
* See the general contract of the <code>readUnsignedByte</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next byte of this input stream, interpreted as an unsigned
* 8-bit number.
* @throws EOFException if this input stream has reached the end.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final int readUnsignedByte() throws IOException {
int ch = in.read();
if (ch < 0)
throw new EOFException();
return ch;
}
/**
* See the general contract of the <code>readShort</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next two bytes of this input stream, interpreted as a signed
* 16-bit number.
* @throws EOFException if this input stream reaches the end before reading two
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final short readShort() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (short) ((ch1 << 0) + (ch2 << 8));
}
/**
* See the general contract of the <code>readUnsignedShort</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next two bytes of this input stream, interpreted as an
* unsigned 16-bit integer.
* @throws EOFException if this input stream reaches the end before reading two
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final int readUnsignedShort() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (ch1 << 0) + (ch2 << 8);
}
/**
* See the general contract of the <code>readChar</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next two bytes of this input stream, interpreted as a
* <code>char</code>.
* @throws EOFException if this input stream reaches the end before reading two
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final char readChar() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (char) ((ch1 << 0) + (ch2 << 8));
}
/**
* See the general contract of the <code>readInt</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next four bytes of this input stream, interpreted as an
* <code>int</code>.
* @throws EOFException if this input stream reaches the end before reading four
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final int readInt() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 0) + (ch2 << 8) + (ch3 << 16) + (ch4 << 24));
}
private byte readBuffer[] = new byte[8];
/**
* See the general contract of the <code>readLong</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next eight bytes of this input stream, interpreted as a
* <code>long</code>.
* @throws EOFException if this input stream reaches the end before reading eight
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see FilterInputStream#in
*/
public final long readLong() throws IOException {
readFully(readBuffer, 0, 8);
return (((long) readBuffer[7] << 56)
+ ((long) (readBuffer[6] & 255) << 48)
+ ((long) (readBuffer[5] & 255) << 40)
+ ((long) (readBuffer[4] & 255) << 32)
+ ((long) (readBuffer[3] & 255) << 24)
+ ((readBuffer[2] & 255) << 16) + ((readBuffer[1] & 255) << 8) + ((readBuffer[0] & 255) << 0));
}
/**
* See the general contract of the <code>readFloat</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next four bytes of this input stream, interpreted as a
* <code>float</code>.
* @throws EOFException if this input stream reaches the end before reading four
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see java.io.DataInputStream#readInt()
* @see Float#intBitsToFloat(int)
*/
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* See the general contract of the <code>readDouble</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return the next eight bytes of this input stream, interpreted as a
* <code>double</code>.
* @throws EOFException if this input stream reaches the end before reading eight
* bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @see java.io.DataInputStream#readLong()
* @see Double#longBitsToDouble(long)
*/
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* See the general contract of the <code>readUTF</code> method of
* <code>DataInput</code>.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @return a Unicode string.
* @throws EOFException if this input stream reaches the end before reading all
* the bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @throws UTFDataFormatException if the bytes do not represent a valid modified UTF-8
* encoding of a string.
* @see java.io.DataInputStream#readUTF(DataInput)
*/
public final String readUTF() throws IOException {
return readUTF(this);
}
/**
* working arrays initialized on demand by readUTF
*/
private byte bytearr[] = new byte[80];
private char chararr[] = new char[80];
/**
* Reads from the stream <code>in</code> a representation of a Unicode
* character string encoded in <a
* href="DataInput.html#modified-utf-8">modified UTF-8</a> format; this
* string of characters is then returned as a <code>String</code>. The
* details of the modified UTF-8 representation are exactly the same as for
* the <code>readUTF</code> method of <code>DataInput</code>.
*
* @param in a data input stream.
* @return a Unicode string.
* @throws EOFException if the input stream reaches the end before all the bytes.
* @throws IOException the stream has been closed and the contained input stream
* does not support reading after close, or another I/O error
* occurs.
* @throws UTFDataFormatException if the bytes do not represent a valid modified UTF-8
* encoding of a Unicode string.
* @see java.io.DataInputStream#readUnsignedShort()
*/
public final static String readUTF(DataInput in) throws IOException {
int utflen = in.readUnsignedShort();
byte[] bytearr = null;
char[] chararr = null;
if (in instanceof LittleEndianDataInputStream) {
LittleEndianDataInputStream dis = (LittleEndianDataInputStream) in;
if (dis.bytearr.length < utflen) {
dis.bytearr = new byte[utflen * 2];
dis.chararr = new char[utflen * 2];
}
chararr = dis.chararr;
bytearr = dis.bytearr;
} else {
bytearr = new byte[utflen];
chararr = new char[utflen];
}
int c, char2, char3;
int count = 0;
int chararr_count = 0;
in.readFully(bytearr, 0, utflen);
while (count < utflen) {
c = (int) bytearr[count] & 0xff;
if (c > 127)
break;
count++;
chararr[chararr_count++] = (char) c;
}
while (count < utflen) {
c = (int) bytearr[count] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
/* 0xxxxxxx */
count++;
chararr[chararr_count++] = (char) c;
break;
case 12:
case 13:
/* 110x xxxx 10xx xxxx */
count += 2;
if (count > utflen)
throw new UTFDataFormatException(
"malformed input: partial character at end");
char2 = (int) bytearr[count - 1];
if ((char2 & 0xC0) != 0x80)
throw new UTFDataFormatException(
"malformed input around byte " + count);
chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
/* 1110 xxxx 10xx xxxx 10xx xxxx */
count += 3;
if (count > utflen)
throw new UTFDataFormatException(
"malformed input: partial character at end");
char2 = (int) bytearr[count - 2];
char3 = (int) bytearr[count - 1];
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
throw new UTFDataFormatException(
"malformed input around byte " + (count - 1));
chararr[chararr_count++] = (char) (((c & 0x0F) << 12)
| ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
break;
default:
/* 10xx xxxx, 1111 xxxx */
throw new UTFDataFormatException("malformed input around byte "
+ count);
}
}
// The number of chars produced may be less than utflen
return new String(chararr, 0, chararr_count);
}
}

@ -0,0 +1,403 @@
/*
* 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.levin.util;
import java.io.DataOutput;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UTFDataFormatException;
/**
* A little endian java.io.DataOutputStream
*/
public class LittleEndianDataOutputStream extends FilterOutputStream implements
DataOutput {
/**
* The number of bytes written to the data output stream so far. If this
* counter overflows, it will be wrapped to Integer.MAX_VALUE.
*/
protected int written;
/**
* Creates a new data output stream to write data to the specified
* underlying output stream. The counter <code>written</code> is set to
* zero.
*
* @param out the underlying output stream, to be saved for later use.
* @see FilterOutputStream#out
*/
public LittleEndianDataOutputStream(OutputStream out) {
super(out);
}
/**
* Increases the written counter by the specified value until it reaches
* Integer.MAX_VALUE.
*/
private void incCount(int value) {
int temp = written + value;
if (temp < 0) {
temp = Integer.MAX_VALUE;
}
written = temp;
}
/**
* Writes the specified byte (the low eight bits of the argument
* <code>b</code>) to the underlying output stream. If no exception is
* thrown, the counter <code>written</code> is incremented by <code>1</code>
* .
* <p>
* Implements the <code>write</code> method of <code>OutputStream</code>.
*
* @param b the <code>byte</code> to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public synchronized void write(int b) throws IOException {
out.write(b);
incCount(1);
}
/**
* Writes <code>len</code> bytes from the specified byte array starting at
* offset <code>off</code> to the underlying output stream. If no exception
* is thrown, the counter <code>written</code> is incremented by
* <code>len</code>.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public synchronized void write(byte b[], int off, int len)
throws IOException {
out.write(b, off, len);
incCount(len);
}
/**
* Flushes this data output stream. This forces any buffered output bytes to
* be written out to the stream.
* <p>
* The <code>flush</code> method of <code>DataOutputStream</code> calls the
* <code>flush</code> method of its underlying output stream.
*
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
* @see OutputStream#flush()
*/
public void flush() throws IOException {
out.flush();
}
/**
* Writes a <code>boolean</code> to the underlying output stream as a 1-byte
* value. The value <code>true</code> is written out as the value
* <code>(byte)1</code>; the value <code>false</code> is written out as the
* value <code>(byte)0</code>. If no exception is thrown, the counter
* <code>written</code> is incremented by <code>1</code>.
*
* @param v a <code>boolean</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeBoolean(boolean v) throws IOException {
out.write(v ? 1 : 0);
incCount(1);
}
/**
* Writes out a <code>byte</code> to the underlying output stream as a
* 1-byte value. If no exception is thrown, the counter <code>written</code>
* is incremented by <code>1</code>.
*
* @param v a <code>byte</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeByte(int v) throws IOException {
out.write(v);
incCount(1);
}
/**
* Writes a <code>short</code> to the underlying output stream as two bytes,
* low byte first. If no exception is thrown, the counter
* <code>written</code> is incremented by <code>2</code>.
*
* @param v a <code>short</code> to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeShort(int v) throws IOException {
out.write((v >>> 0) & 0xFF);
out.write((v >>> 8) & 0xFF);
incCount(2);
}
/**
* Writes a <code>char</code> to the underlying output stream as a 2-byte
* value, low byte first. If no exception is thrown, the counter
* <code>written</code> is incremented by <code>2</code>.
*
* @param v a <code>char</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeChar(int v) throws IOException {
out.write((v >>> 0) & 0xFF);
out.write((v >>> 8) & 0xFF);
incCount(2);
}
/**
* Writes an <code>int</code> to the underlying output stream as four bytes,
* low byte first. If no exception is thrown, the counter
* <code>written</code> is incremented by <code>4</code>.
*
* @param v an <code>int</code> to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeInt(int v) throws IOException {
out.write((v >>> 0) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 24) & 0xFF);
incCount(4);
}
private byte writeBuffer[] = new byte[8];
/**
* Writes a <code>long</code> to the underlying output stream as eight
* bytes, low byte first. In no exception is thrown, the counter
* <code>written</code> is incremented by <code>8</code>.
*
* @param v a <code>long</code> to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeLong(long v) throws IOException {
writeBuffer[7] = (byte) (v >>> 56);
writeBuffer[6] = (byte) (v >>> 48);
writeBuffer[5] = (byte) (v >>> 40);
writeBuffer[4] = (byte) (v >>> 32);
writeBuffer[3] = (byte) (v >>> 24);
writeBuffer[2] = (byte) (v >>> 16);
writeBuffer[1] = (byte) (v >>> 8);
writeBuffer[0] = (byte) (v >>> 0);
out.write(writeBuffer, 0, 8);
incCount(8);
}
/**
* Converts the float argument to an <code>int</code> using the
* <code>floatToIntBits</code> method in class <code>Float</code>, and then
* writes that <code>int</code> value to the underlying output stream as a
* 4-byte quantity, low byte first. If no exception is thrown, the counter
* <code>written</code> is incremented by <code>4</code>.
*
* @param v a <code>float</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
* @see Float#floatToIntBits(float)
*/
public final void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
}
/**
* Converts the double argument to a <code>long</code> using the
* <code>doubleToLongBits</code> method in class <code>Double</code>, and
* then writes that <code>long</code> value to the underlying output stream
* as an 8-byte quantity, low byte first. If no exception is thrown, the
* counter <code>written</code> is incremented by <code>8</code>.
*
* @param v a <code>double</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
* @see Double#doubleToLongBits(double)
*/
public final void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
}
/**
* Writes out the string to the underlying output stream as a sequence of
* bytes. Each character in the string is written out, in sequence, by
* discarding its high eight bits. If no exception is thrown, the counter
* <code>written</code> is incremented by the length of <code>s</code>.
*
* @param s a string of bytes to be written.
* @throws IOException if an I/O error occurs.
* @see FilterOutputStream#out
*/
public final void writeBytes(String s) throws IOException {
int len = s.length();
for (int i = 0; i < len; i++) {
out.write((byte) s.charAt(i));
}
incCount(len);
}
/**
* Writes a string to the underlying output stream as a sequence of
* characters. Each character is written to the data output stream as if by
* the <code>writeChar</code> method. If no exception is thrown, the counter
* <code>written</code> is incremented by twice the length of <code>s</code>
* .
*
* @param s a <code>String</code> value to be written.
* @throws IOException if an I/O error occurs.
* @see java.io.DataOutputStream#writeChar(int)
* @see FilterOutputStream#out
*/
public final void writeChars(String s) throws IOException {
int len = s.length();
for (int i = 0; i < len; i++) {
int v = s.charAt(i);
out.write((v >>> 0) & 0xFF);
out.write((v >>> 8) & 0xFF);
}
incCount(len * 2);
}
/**
* Writes a string to the underlying output stream using <a
* href="DataInput.html#modified-utf-8">modified UTF-8</a> encoding in a
* machine-independent manner.
* <p>
* First, two bytes are written to the output stream as if by the
* <code>writeShort</code> method giving the number of bytes to follow. This
* value is the number of bytes actually written out, not the length of the
* string. Following the length, each character of the string is output, in
* sequence, using the modified UTF-8 encoding for the character. If no
* exception is thrown, the counter <code>written</code> is incremented by
* the total number of bytes written to the output stream. This will be at
* least two plus the length of <code>str</code>, and at most two plus
* thrice the length of <code>str</code>.
*
* @param str a string to be written.
* @throws IOException if an I/O error occurs.
*/
public final void writeUTF(String str) throws IOException {
writeUTF(str, this);
}
/**
* bytearr is initialized on demand by writeUTF
*/
private byte[] bytearr = null;
/**
* Writes a string to the specified DataOutput using <a
* href="DataInput.html#modified-utf-8">modified UTF-8</a> encoding in a
* machine-independent manner.
* <p>
* First, two bytes are written to out as if by the <code>writeShort</code>
* method giving the number of bytes to follow. This value is the number of
* bytes actually written out, not the length of the string. Following the
* length, each character of the string is output, in sequence, using the
* modified UTF-8 encoding for the character. If no exception is thrown, the
* counter <code>written</code> is incremented by the total number of bytes
* written to the output stream. This will be at least two plus the length
* of <code>str</code>, and at most two plus thrice the length of
* <code>str</code>.
*
* @param str a string to be written.
* @param out destination to write to
* @return The number of bytes written out.
* @throws IOException if an I/O error occurs.
*/
static int writeUTF(String str, DataOutput out) throws IOException {
int strlen = str.length();
int utflen = 0;
int c, count = 0;
/* use charAt instead of copying String to char array */
for (int i = 0; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
if (utflen > 65535)
throw new UTFDataFormatException("encoded string too long: "
+ utflen + " bytes");
byte[] bytearr = null;
if (out instanceof LittleEndianDataOutputStream) {
LittleEndianDataOutputStream dos = (LittleEndianDataOutputStream) out;
if (dos.bytearr == null || (dos.bytearr.length < (utflen + 2)))
dos.bytearr = new byte[(utflen * 2) + 2];
bytearr = dos.bytearr;
} else {
bytearr = new byte[utflen + 2];
}
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
int i = 0;
for (i = 0; i < strlen; i++) {
c = str.charAt(i);
if (!((c >= 0x0001) && (c <= 0x007F)))
break;
bytearr[count++] = (byte) c;
}
for (; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
bytearr[count++] = (byte) c;
} else if (c > 0x07FF) {
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
} else {
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
out.write(bytearr, 0, utflen + 2);
return utflen + 2;
}
/**
* Returns the current value of the counter <code>written</code>, the number
* of bytes written to this data output stream so far. If the counter
* overflows, it will be wrapped to Integer.MAX_VALUE.
*
* @return the value of the <code>written</code> field.
* @see java.io.DataOutputStream#written
*/
public final int size() {
return written;
}
}

@ -85,15 +85,15 @@ public class GenerateFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_generate, container, false);
etWalletName = (TextInputLayout) view.findViewById(R.id.etWalletName);
etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword);
llFingerprintAuth = (LinearLayout) view.findViewById(R.id.llFingerprintAuth);
etWalletMnemonic = (TextInputLayout) view.findViewById(R.id.etWalletMnemonic);
etWalletAddress = (TextInputLayout) view.findViewById(R.id.etWalletAddress);
etWalletViewKey = (TextInputLayout) view.findViewById(R.id.etWalletViewKey);
etWalletSpendKey = (TextInputLayout) view.findViewById(R.id.etWalletSpendKey);
etWalletRestoreHeight = (TextInputLayout) view.findViewById(R.id.etWalletRestoreHeight);
bGenerate = (Button) view.findViewById(R.id.bGenerate);
etWalletName = view.findViewById(R.id.etWalletName);
etWalletPassword = view.findViewById(R.id.etWalletPassword);
llFingerprintAuth = view.findViewById(R.id.llFingerprintAuth);
etWalletMnemonic = view.findViewById(R.id.etWalletMnemonic);
etWalletAddress = view.findViewById(R.id.etWalletAddress);
etWalletViewKey = view.findViewById(R.id.etWalletViewKey);
etWalletSpendKey = view.findViewById(R.id.etWalletSpendKey);
etWalletRestoreHeight = view.findViewById(R.id.etWalletRestoreHeight);
bGenerate = view.findViewById(R.id.bGenerate);
etWalletMnemonic.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT);
etWalletAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

@ -91,22 +91,22 @@ public class GenerateReviewFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_review, container, false);
scrollview = (ScrollView) view.findViewById(R.id.scrollview);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
tvWalletPassword = (TextView) view.findViewById(R.id.tvWalletPassword);
tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress);
tvWalletViewKey = (TextView) view.findViewById(R.id.tvWalletViewKey);
tvWalletSpendKey = (TextView) view.findViewById(R.id.tvWalletSpendKey);
tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic);
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
bAdvancedInfo = (Button) view.findViewById(R.id.bAdvancedInfo);
llAdvancedInfo = (LinearLayout) view.findViewById(R.id.llAdvancedInfo);
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);
scrollview = view.findViewById(R.id.scrollview);
pbProgress = view.findViewById(R.id.pbProgress);
tvWalletPassword = view.findViewById(R.id.tvWalletPassword);
tvWalletAddress = view.findViewById(R.id.tvWalletAddress);
tvWalletViewKey = view.findViewById(R.id.tvWalletViewKey);
tvWalletSpendKey = view.findViewById(R.id.tvWalletSpendKey);
tvWalletMnemonic = view.findViewById(R.id.tvWalletMnemonic);
bCopyAddress = view.findViewById(R.id.bCopyAddress);
bAdvancedInfo = view.findViewById(R.id.bAdvancedInfo);
llAdvancedInfo = view.findViewById(R.id.llAdvancedInfo);
llPassword = view.findViewById(R.id.llPassword);
llMnemonic = view.findViewById(R.id.llMnemonic);
llSpendKey = view.findViewById(R.id.llSpendKey);
llViewKey = view.findViewById(R.id.llViewKey);
bAccept = view.findViewById(R.id.bAccept);
boolean allowCopy = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
tvWalletMnemonic.setTextIsSelectable(allowCopy);
@ -481,13 +481,13 @@ public class GenerateReviewFragment extends Fragment {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
alertDialogBuilder.setView(promptsView);
final TextInputLayout etPasswordA = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordA);
final TextInputLayout etPasswordA = promptsView.findViewById(R.id.etWalletPasswordA);
etPasswordA.setHint(getString(R.string.prompt_changepw, walletName));
final TextInputLayout etPasswordB = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordB);
final TextInputLayout etPasswordB = promptsView.findViewById(R.id.etWalletPasswordB);
etPasswordB.setHint(getString(R.string.prompt_changepwB, walletName));
LinearLayout llFingerprintAuth = (LinearLayout) promptsView.findViewById(R.id.llFingerprintAuth);
LinearLayout llFingerprintAuth = promptsView.findViewById(R.id.llFingerprintAuth);
final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0);
if (FingerprintHelper.isDeviceSupported(getActivity())) {
llFingerprintAuth.setVisibility(View.VISIBLE);

@ -43,7 +43,8 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.data.WalletNode;
import com.m2049r.xmrwallet.data.Node;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.dialog.AboutFragment;
import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment;
@ -64,25 +65,136 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import timber.log.Timber;
public class LoginActivity extends BaseActivity
implements LoginFragment.Listener, GenerateFragment.Listener,
GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener,
ReceiveFragment.Listener {
ReceiveFragment.Listener, NodeFragment.Listener {
private static final String GENERATE_STACK = "gen";
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
private static final String NODES_PREFS_NAME = "nodes";
private static final String PREF_DAEMON_STAGENET = "daemon_stagenet";
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
private NodeInfo node = null;
Set<NodeInfo> favouriteNodes = new HashSet<>();
@Override
public void setNode(NodeInfo node) {
if ((node != null) && (node.getNetworkType() != WalletManager.getInstance().getNetworkType()))
throw new IllegalArgumentException("network type does not match");
this.node = node;
WalletManager.getInstance().setDaemon(node);
}
@Override
public Set<NodeInfo> getFavouriteNodes() {
return favouriteNodes;
}
@Override
public void setFavouriteNodes(Set<NodeInfo> nodes) {
Timber.d("adding %d nodes", nodes.size());
favouriteNodes.clear();
for (NodeInfo node : nodes) {
Timber.d("adding %s %b", node, node.isFavourite());
if (node.isFavourite())
favouriteNodes.add(node);
}
if (favouriteNodes.isEmpty() && (!nodes.isEmpty())) { // no favourites - pick best ones
List<NodeInfo> nodeList = new ArrayList<>(nodes);
Collections.sort(nodeList, NodeInfo.BestNodeComparator);
int i = 0;
for (NodeInfo node : nodeList) {
Timber.d("adding %s", node);
node.setFavourite(true);
favouriteNodes.add(node);
if (++i >= 3) break; // add max first 3 nodes
}
Toast.makeText(this, getString(R.string.node_nobookmark, i), Toast.LENGTH_LONG).show();
}
saveFavourites();
}
private void loadFavouritesWithNetwork() {
Helper.runWithNetwork(new Helper.Action() {
@Override
public boolean run() {
loadFavourites();
return true;
}
});
}
private void loadFavourites() {
Timber.d("loadFavourites");
favouriteNodes.clear();
Map<String, ?> storedNodes = getSharedPreferences(NODES_PREFS_NAME, Context.MODE_PRIVATE).getAll();
for (Map.Entry<String, ?> nodeEntry : storedNodes.entrySet()) {
if (nodeEntry != null) // just in case, ignore possible future errors
addFavourite((String) nodeEntry.getValue());
}
if (storedNodes.isEmpty()) { // try to load legacy list & remove it (i.e. migrate the data once)
SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
loadLegacyList(sharedPref.getString(PREF_DAEMON_MAINNET, null));
sharedPref.edit().remove(PREF_DAEMON_MAINNET).apply();
break;
case NetworkType_Stagenet:
loadLegacyList(sharedPref.getString(PREF_DAEMON_STAGENET, null));
sharedPref.edit().remove(PREF_DAEMON_STAGENET).apply();
break;
default:
throw new IllegalStateException("unsupported net " + WalletManager.getInstance().getNetworkType());
}
}
}
private void saveFavourites() {
List<Node> favourites = new ArrayList<>();
Timber.d("SAVE");
SharedPreferences.Editor editor = getSharedPreferences(NODES_PREFS_NAME, Context.MODE_PRIVATE).edit();
editor.clear();
int i = 1;
for (Node info : favouriteNodes) {
String nodeString = info.toNodeString();
editor.putString(Integer.toString(i), nodeString);
Timber.d("saved %d:%s", i, nodeString);
i++;
}
editor.apply();
}
private void addFavourite(String nodeString) {
NodeInfo nodeInfo = NodeInfo.fromString(nodeString);
if (nodeInfo != null) {
nodeInfo.setFavourite(true);
favouriteNodes.add(nodeInfo);
} else
Timber.w("nodeString invalid: %s", nodeString);
}
private void loadLegacyList(final String legacyListString) {
if (legacyListString == null) return;
final String[] nodeStrings = legacyListString.split(";");
for (final String nodeString : nodeStrings) {
addFavourite(nodeString);
}
}
private Toolbar toolbar;
@ -120,7 +232,7 @@ public class LoginActivity extends BaseActivity
}
setContentView(R.layout.activity_login);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
@ -138,12 +250,15 @@ public class LoginActivity extends BaseActivity
CreditsFragment.display(getSupportFragmentManager());
break;
case Toolbar.BUTTON_NONE:
break;
default:
Timber.e("Button " + type + "pressed - how can this be?");
}
}
});
loadFavouritesWithNetwork();
if (Helper.getWritePermission(this)) {
if (savedInstanceState == null) startLoginFragment();
} else {
@ -162,15 +277,14 @@ public class LoginActivity extends BaseActivity
}
@Override
public boolean onWalletSelected(String walletName, String daemon, boolean streetmode) {
if (daemon.length() == 0) {
public boolean onWalletSelected(String walletName, boolean streetmode) {
if (node == null) {
Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show();
return false;
}
if (checkServiceRunning()) return false;
try {
WalletNode aWalletNode = new WalletNode(walletName, daemon, WalletManager.getInstance().getNetworkType());
new AsyncOpenWallet(streetmode).execute(aWalletNode);
new AsyncOpenWallet(walletName, node, streetmode).execute();
} catch (IllegalArgumentException ex) {
Timber.e(ex.getLocalizedMessage());
Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
@ -211,7 +325,7 @@ public class LoginActivity extends BaseActivity
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getString(R.string.details_alert_message))
AlertDialog diag = builder.setMessage(getString(R.string.details_alert_message))
.setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener)
.setNegativeButton(getString(R.string.details_alert_no), dialogClickListener)
.show();
@ -298,8 +412,8 @@ public class LoginActivity extends BaseActivity
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setView(promptsView);
final EditText etRename = (EditText) promptsView.findViewById(R.id.etRename);
final TextView tvRenameLabel = (TextView) promptsView.findViewById(R.id.tvRenameLabel);
final EditText etRename = promptsView.findViewById(R.id.etRename);
final TextView tvRenameLabel = promptsView.findViewById(R.id.tvRenameLabel);
tvRenameLabel.setText(getString(R.string.prompt_rename, walletName));
@ -488,13 +602,16 @@ public class LoginActivity extends BaseActivity
startGenerateFragment(type);
}
@Override
public void onNodePrefs() {
Timber.d("node prefs");
if (checkServiceRunning()) return;
startNodeFragment();
}
////////////////////////////////////////
// LoginFragment.Listener
////////////////////////////////////////
@Override
public SharedPreferences getPrefs() {
return getPreferences(Context.MODE_PRIVATE);
}
@Override
public File getStorageRoot() {
@ -506,9 +623,13 @@ public class LoginActivity extends BaseActivity
@Override
public void showNet() {
switch (WalletManager.getInstance().getNetworkType()) {
showNet(WalletManager.getInstance().getNetworkType());
}
private void showNet(NetworkType net) {
switch (net) {
case NetworkType_Mainnet:
toolbar.setSubtitle(getString(R.string.connect_mainnet));
toolbar.setSubtitle(null);
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
break;
case NetworkType_Testnet:
@ -520,7 +641,7 @@ public class LoginActivity extends BaseActivity
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
default:
throw new IllegalStateException("NetworkType unknown: " + WalletManager.getInstance().getNetworkType());
throw new IllegalStateException("NetworkType unknown: " + net);
}
}
@ -609,7 +730,8 @@ public class LoginActivity extends BaseActivity
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult()");
switch (requestCode) {
case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE:
@ -660,6 +782,11 @@ public class LoginActivity extends BaseActivity
Timber.d("GenerateReviewFragment placed");
}
void startNodeFragment() {
replaceFragment(new NodeFragment(), null, null);
Timber.d("NodeFragment placed");
}
void startReceiveFragment(Bundle extras) {
replaceFragment(new ReceiveFragment(), null, extras);
Timber.d("ReceiveFragment placed");
@ -826,7 +953,8 @@ public class LoginActivity extends BaseActivity
}
@Override
public void onGenerateLedger(final String name, final String password, final long restoreHeight) {
public void onGenerateLedger(final String name, final String password,
final long restoreHeight) {
createWallet(name, password,
new WalletCreator() {
@Override
@ -921,7 +1049,8 @@ public class LoginActivity extends BaseActivity
}
}
boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite, boolean ignoreCacheError) {
boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite,
boolean ignoreCacheError) {
if (walletExists(dstWallet, true) && !overwrite) return false;
boolean success = false;
File srcDir = srcWallet.getParentFile();
@ -1029,6 +1158,12 @@ public class LoginActivity extends BaseActivity
if (((GenerateReviewFragment) f).backOk()) {
super.onBackPressed();
}
} else if (f instanceof NodeFragment) {
if (!((NodeFragment) f).isRefreshing()) {
super.onBackPressed();
} else {
Toast.makeText(LoginActivity.this, getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
}
} else if (f instanceof LoginFragment) {
if (((LoginFragment) f).isFabOpen()) {
((LoginFragment) f).animateFAB();
@ -1070,101 +1205,64 @@ public class LoginActivity extends BaseActivity
case R.id.action_help_list:
HelpFragment.display(getSupportFragmentManager(), R.string.help_list);
return true;
case R.id.action_help_node:
HelpFragment.display(getSupportFragmentManager(), R.string.help_node);
return true;
case R.id.action_privacy_policy:
PrivacyFragment.display(getSupportFragmentManager());
return true;
case R.id.action_language:
onChangeLocale();
return true;
case R.id.action_stagenet:
try {
LoginFragment loginFragment = (LoginFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
item.setChecked(loginFragment.onStagenetMenuItem());
} catch (ClassCastException ex) {
// never mind then
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void setNetworkType(NetworkType networkType) {
WalletManager.getInstance().setNetworkType(networkType);
}
private class AsyncOpenWallet extends AsyncTask<WalletNode, Void, Integer> {
// an AsyncTask which tests the node before trying to open the wallet
private class AsyncOpenWallet extends AsyncTask<Void, Void, Boolean> {
final static int OK = 0;
final static int TIMEOUT = 1;
final static int INVALID = 2;
final static int IOEX = 3;
private WalletNode walletNode;
private final String walletName;
private final NodeInfo node;
private final boolean streetmode;
public AsyncOpenWallet(boolean streetmode) {
AsyncOpenWallet(String walletName, NodeInfo node, boolean streetmode) {
this.walletName = walletName;
this.node = node;
this.streetmode = streetmode;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4);
}
@Override
protected Integer doInBackground(WalletNode... params) {
if (params.length != 1) return INVALID;
this.walletNode = params[0];
if (!walletNode.isValid()) return INVALID;
Timber.d("checking %s", walletNode.getAddress());
try {
long timeDA = new Date().getTime();
SocketAddress address = walletNode.getSocketAddress();
long timeDB = new Date().getTime();
Timber.d("Resolving " + walletNode.getAddress() + " took " + (timeDB - timeDA) + "ms.");
Socket socket = new Socket();
long timeA = new Date().getTime();
socket.connect(address, LoginActivity.DAEMON_TIMEOUT);
socket.close();
long timeB = new Date().getTime();
long time = timeB - timeA;
Timber.d("Daemon " + walletNode.getAddress() + " is " + time + "ms away.");
return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT);
} catch (IOException ex) {
Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage());
return IOEX;
} catch (IllegalArgumentException ex) {
Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage());
return INVALID;
}
protected Boolean doInBackground(Void... params) {
Timber.d("checking %s", node.getAddress());
return node.testRpcService();
}
@Override
protected void onPostExecute(Integer result) {
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (isDestroyed()) {
return;
}
dismissProgressDialog();
switch (result) {
case OK:
Timber.d("selected wallet is .%s.", walletNode.getName());
// now it's getting real, onValidateFields if wallet exists
promptAndStart(walletNode, streetmode);
break;
case TIMEOUT:
Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show();
break;
case INVALID:
if (result) {
Timber.d("selected wallet is .%s.", node.getName());
// now it's getting real, onValidateFields if wallet exists
promptAndStart(walletName, node, streetmode);
} else {
if (node.getResponseCode() == 0) { // IOException
Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show();
break;
case IOEX:
} else { // connected but broken
Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show();
break;
}
}
}
}
@ -1191,23 +1289,20 @@ public class LoginActivity extends BaseActivity
return false;
}
void promptAndStart(WalletNode walletNode, final boolean streetmode) {
File walletFile = Helper.getWalletFile(this, walletNode.getName());
void promptAndStart(String walletName, Node node, final boolean streetmode) {
File walletFile = Helper.getWalletFile(this, walletName);
if (WalletManager.getInstance().walletExists(walletFile)) {
WalletManager.getInstance().setDaemon(walletNode);
Helper.promptPassword(LoginActivity.this, walletNode.getName(), false,
Helper.promptPassword(LoginActivity.this, walletName, false,
new Helper.PasswordAction() {
@Override
public void action(String walletName, String password, boolean fingerprintUsed) {
if (checkDevice(walletName, password))
startWallet(walletName, password, fingerprintUsed, streetmode);
}
});
} else { // this cannot really happen as we prefilter choices
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
}
}
// USB Stuff - (Ledger)

@ -17,14 +17,13 @@
package com.m2049r.xmrwallet;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -33,28 +32,24 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.layout.NodeInfoAdapter;
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.NodeList;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.widget.DropDownEditText;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@ -68,20 +63,19 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private List<WalletManager.WalletInfo> walletList = new ArrayList<>();
private List<WalletManager.WalletInfo> displayedList = new ArrayList<>();
private EditText etDummy;
private ImageView ivGunther;
private DropDownEditText etDaemonAddress;
private ArrayAdapter<String> nodeAdapter;
private TextView tvNodeName;
private TextView tvNodeAddress;
private View pbNode;
private View llNode;
private Listener activityCallback;
// Container Activity must implement this interface
public interface Listener {
SharedPreferences getPrefs();
File getStorageRoot();
boolean onWalletSelected(String wallet, String daemon, boolean streetmode);
boolean onWalletSelected(String wallet, boolean streetmode);
void onWalletDetails(String wallet);
@ -95,13 +89,17 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
void onAddWallet(String type);
void onNodePrefs();
void showNet();
void setToolbarButton(int type);
void setTitle(String title);
void setNetworkType(NetworkType networkType);
void setNode(NodeInfo node);
Set<NodeInfo> getFavouriteNodes();
boolean hasLedger();
}
@ -120,17 +118,17 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override
public void onPause() {
Timber.d("onPause()");
savePrefs();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
Timber.d("onResume() %s", activityCallback.getFavouriteNodes().size());
activityCallback.setTitle(null);
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
activityCallback.showNet();
findBestNode();
}
@Override
@ -139,20 +137,20 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
Timber.d("onCreateView");
View view = inflater.inflate(R.layout.fragment_login, container, false);
ivGunther = (ImageView) view.findViewById(R.id.ivGunther);
fabScreen = (FrameLayout) view.findViewById(R.id.fabScreen);
fab = (FloatingActionButton) view.findViewById(R.id.fab);
fabNew = (FloatingActionButton) view.findViewById(R.id.fabNew);
fabView = (FloatingActionButton) view.findViewById(R.id.fabView);
fabKey = (FloatingActionButton) view.findViewById(R.id.fabKey);
fabSeed = (FloatingActionButton) view.findViewById(R.id.fabSeed);
fabLedger = (FloatingActionButton) view.findViewById(R.id.fabLedger);
fabNewL = (RelativeLayout) view.findViewById(R.id.fabNewL);
fabViewL = (RelativeLayout) view.findViewById(R.id.fabViewL);
fabKeyL = (RelativeLayout) view.findViewById(R.id.fabKeyL);
fabSeedL = (RelativeLayout) view.findViewById(R.id.fabSeedL);
fabLedgerL = (RelativeLayout) view.findViewById(R.id.fabLedgerL);
ivGunther = view.findViewById(R.id.ivGunther);
fabScreen = view.findViewById(R.id.fabScreen);
fab = view.findViewById(R.id.fab);
fabNew = view.findViewById(R.id.fabNew);
fabView = view.findViewById(R.id.fabView);
fabKey = view.findViewById(R.id.fabKey);
fabSeed = view.findViewById(R.id.fabSeed);
fabLedger = view.findViewById(R.id.fabLedger);
fabNewL = view.findViewById(R.id.fabNewL);
fabViewL = view.findViewById(R.id.fabViewL);
fabKeyL = view.findViewById(R.id.fabKeyL);
fabSeedL = view.findViewById(R.id.fabSeedL);
fabLedgerL = view.findViewById(R.id.fabLedgerL);
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
@ -169,71 +167,48 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabLedger.setOnClickListener(this);
fabScreen.setOnClickListener(this);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
RecyclerView recyclerView = view.findViewById(R.id.list);
registerForContextMenu(recyclerView);
this.adapter = new WalletInfoAdapter(getActivity(), this);
recyclerView.setAdapter(adapter);
etDummy = (EditText) view.findViewById(R.id.etDummy);
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
ViewGroup llNotice = view.findViewById(R.id.llNotice);
Notice.showAll(llNotice, ".*_login");
etDaemonAddress = (DropDownEditText) view.findViewById(R.id.etDaemonAddress);
nodeAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line);
etDaemonAddress.setAdapter(nodeAdapter);
Helper.hideKeyboard(getActivity());
etDaemonAddress.setThreshold(0);
etDaemonAddress.setOnClickListener(new View.OnClickListener() {
pbNode = view.findViewById(R.id.pbNode);
llNode = view.findViewById(R.id.llNode);
llNode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
etDaemonAddress.showDropDown();
Helper.showKeyboard(getActivity());
if (activityCallback.getFavouriteNodes().isEmpty())
activityCallback.onNodePrefs();
else
findBestNode();
}
});
etDaemonAddress.setOnFocusChangeListener(new View.OnFocusChangeListener() {
tvNodeName = view.findViewById(R.id.tvNodeName);
tvNodeAddress = view.findViewById(R.id.tvNodeAddress);
view.findViewById(R.id.ibOption).setOnClickListener(new View.OnClickListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus && !getActivity().isFinishing() && etDaemonAddress.isLaidOut()) {
etDaemonAddress.showDropDown();
Helper.showKeyboard(getActivity());
}
}
});
etDaemonAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
Helper.hideKeyboard(getActivity());
etDummy.requestFocus();
return true;
}
return false;
public void onClick(View v) {
if (activityCallback != null)
activityCallback.onNodePrefs();
}
});
etDaemonAddress.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View arg1, int pos, long id) {
Helper.hideKeyboard(getActivity());
etDummy.requestFocus();
}
});
Helper.hideKeyboard(getActivity());
loadPrefs();
loadList();
return view;
}
// Callbacks from WalletInfoAdapter
// Wallet touched
@Override
public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) {
String addressPrefix = addressPrefix();
String addressPrefix = WalletManager.getInstance().addressPrefix();
if (addressPrefix.indexOf(infoItem.address.charAt(0)) < 0) {
Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show();
return;
@ -242,9 +217,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
private void openWallet(String name, boolean streetmode) {
if (activityCallback.onWalletSelected(name, getDaemon(), streetmode)) {
savePrefs();
}
activityCallback.onWalletSelected(name, streetmode);
}
@Override
@ -274,22 +247,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
return true;
}
private String addressPrefix() {
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Testnet:
return "9A-";
case NetworkType_Mainnet:
return "4-";
case NetworkType_Stagenet:
return "5-";
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
}
}
private void filterList() {
displayedList.clear();
String addressPrefix = addressPrefix();
String addressPrefix = WalletManager.getInstance().addressPrefix();
for (WalletManager.WalletInfo s : walletList) {
if (addressPrefix.indexOf(s.address.charAt(0)) >= 0) displayedList.add(s);
}
@ -348,94 +308,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.list_menu, menu);
menu.findItem(R.id.action_stagenet).setChecked(stagenetCheckMenu);
super.onCreateOptionsMenu(menu, inflater);
}
private boolean stagenetCheckMenu = BuildConfig.DEBUG;
public boolean onStagenetMenuItem() {
boolean lastState = stagenetCheckMenu;
setNet(!lastState, true); // set and save
return !lastState;
}
public void setNet(boolean stagenetChecked, boolean save) {
this.stagenetCheckMenu = stagenetChecked;
NetworkType net = stagenetChecked ? NetworkType.NetworkType_Stagenet : NetworkType.NetworkType_Mainnet;
activityCallback.setNetworkType(net);
activityCallback.showNet();
if (save) {
savePrefs(true); // use previous state as we just clicked it
}
if (stagenetChecked) {
setDaemon(daemonStageNet);
} else {
setDaemon(daemonMainNet);
}
loadList();
}
private static final String PREF_DAEMON_STAGENET = "daemon_stagenet";
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
private static final String PREF_DAEMONLIST_MAINNET =
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
private static final String PREF_DAEMONLIST_STAGENET =
"stagenet.monerujo.io;stagenet.xmr-tw.org";
private NodeList daemonStageNet;
private NodeList daemonMainNet;
void loadPrefs() {
SharedPreferences sharedPref = activityCallback.getPrefs();
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
daemonStageNet = new NodeList(sharedPref.getString(PREF_DAEMON_STAGENET, PREF_DAEMONLIST_STAGENET));
setNet(stagenetCheckMenu, false);
}
void savePrefs() {
savePrefs(false);
}
void savePrefs(boolean usePreviousNetState) {
Timber.d("SAVE / %s", usePreviousNetState);
// save the daemon address for the net
boolean stagenet = stagenetCheckMenu ^ usePreviousNetState;
String daemon = getDaemon();
if (stagenet) {
daemonStageNet.setRecent(daemon);
} else {
daemonMainNet.setRecent(daemon);
}
SharedPreferences sharedPref = activityCallback.getPrefs();
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(PREF_DAEMON_MAINNET, daemonMainNet.toString());
editor.putString(PREF_DAEMON_STAGENET, daemonStageNet.toString());
editor.apply();
}
String getDaemon() {
return etDaemonAddress.getText().toString().trim();
}
void setDaemon(NodeList nodeList) {
Timber.d("setDaemon() %s", nodeList.toString());
String[] nodes = nodeList.getNodes().toArray(new String[0]);
nodeAdapter.clear();
nodeAdapter.addAll(nodes);
etDaemonAddress.getText().clear();
if (nodes.length > 0) {
etDaemonAddress.setText(nodes[0]);
}
etDaemonAddress.dismissDropDown();
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());
}
private boolean isFabOpen = false;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger;
private FrameLayout fabScreen;
@ -534,4 +409,71 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
break;
}
}
public void findBestNode() {
new AsyncFindBestNode().execute();
}
private class AsyncFindBestNode extends AsyncTask<Void, Void, NodeInfo> {
@Override
protected void onPreExecute() {
super.onPreExecute();
pbNode.setVisibility(View.VISIBLE);
llNode.setVisibility(View.INVISIBLE);
activityCallback.setNode(null);
}
@Override
protected NodeInfo doInBackground(Void... params) {
List<NodeInfo> nodesToTest = new ArrayList<>(activityCallback.getFavouriteNodes());
Timber.d("testing best node from %d", nodesToTest.size());
if (nodesToTest.isEmpty()) return null;
for (NodeInfo node : nodesToTest) {
node.testRpcService(); // TODO: do this in parallel?
// no: it's better if it looks like it's doing something
}
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
NodeInfo bestNode = nodesToTest.get(0);
if (bestNode.isValid())
return nodesToTest.get(0);
else
return null;
}
@Override
protected void onPostExecute(NodeInfo result) {
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);
} else {
if (!activityCallback.getFavouriteNodes().isEmpty()) {
tvNodeName.setText(getResources().getText(R.string.node_refresh_hint));
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_refresh_black_24dp, 0, 0, 0);
tvNodeAddress.setText(null);
tvNodeAddress.setVisibility(View.GONE);
} else {
tvNodeName.setText(getResources().getText(R.string.node_create_hint));
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
tvNodeAddress.setText(null);
tvNodeAddress.setVisibility(View.GONE);
}
}
}
@Override
protected void onCancelled(NodeInfo result) { //TODO: cancel this on exit from fragment
Timber.d("cancelled with %s", result);
}
}
private void showNode(NodeInfo nodeInfo) {
tvNodeName.setText(nodeInfo.getName());
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(NodeInfoAdapter.getPingIcon(nodeInfo), 0, 0, 0);
tvNodeAddress.setText(nodeInfo.getAddress());
tvNodeAddress.setVisibility(View.VISIBLE);
}
}

@ -0,0 +1,550 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.levin.scanner.Dispatcher;
import com.m2049r.xmrwallet.data.Node;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.layout.NodeInfoAdapter;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.text.NumberFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import timber.log.Timber;
public class NodeFragment extends Fragment
implements NodeInfoAdapter.OnInteractionListener, View.OnClickListener {
static private int NODES_TO_FIND = 10;
static private NumberFormat FORMATTER = NumberFormat.getInstance();
private SwipeRefreshLayout pullToRefresh;
private TextView tvPull;
private View fab;
private Set<NodeInfo> nodeList = new HashSet<>();
private NodeInfoAdapter nodesAdapter;
private Listener activityCallback;
public interface Listener {
File getStorageRoot();
void setToolbarButton(int type);
void setSubtitle(String title);
Set<NodeInfo> getFavouriteNodes();
void setFavouriteNodes(Set<NodeInfo> favouriteNodes);
}
void filterFavourites() {
for (Iterator<NodeInfo> iter = nodeList.iterator(); iter.hasNext(); ) {
Node node = iter.next();
if (!node.isFavourite()) iter.remove();
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onPause() {
Timber.d("onPause() %d", nodeList.size());
if (asyncFindNodes != null)
asyncFindNodes.cancel(true);
if (activityCallback != null)
activityCallback.setFavouriteNodes(nodeList);
super.onPause();
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activityCallback.setSubtitle(getString(R.string.label_nodes));
updateRefreshElements();
}
boolean isRefreshing() {
return asyncFindNodes != null;
}
void updateRefreshElements() {
if (isRefreshing()) {
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
fab.setVisibility(View.GONE);
} else {
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
fab.setVisibility(View.VISIBLE);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Timber.d("onCreateView");
View view = inflater.inflate(R.layout.fragment_node, container, false);
fab = view.findViewById(R.id.fab);
fab.setOnClickListener(this);
RecyclerView recyclerView = view.findViewById(R.id.list);
nodesAdapter = new NodeInfoAdapter(getActivity(), this);
recyclerView.setAdapter(nodesAdapter);
tvPull = view.findViewById(R.id.tvPull);
pullToRefresh = view.findViewById(R.id.pullToRefresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
if (WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) {
refresh();
} else {
Toast.makeText(getActivity(), getString(R.string.node_wrong_net), Toast.LENGTH_LONG).show();
pullToRefresh.setRefreshing(false);
}
}
});
Helper.hideKeyboard(getActivity());
nodeList = new HashSet<>(activityCallback.getFavouriteNodes());
nodesAdapter.setNodes(nodeList);
ViewGroup llNotice = view.findViewById(R.id.llNotice);
Notice.showAll(llNotice, ".*_nodes");
return view;
}
private AsyncFindNodes asyncFindNodes = null;
private void refresh() {
if (asyncFindNodes != null) return; // ignore refresh request as one is ongoing
asyncFindNodes = new AsyncFindNodes();
updateRefreshElements();
asyncFindNodes.execute();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.node_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
// Callbacks from NodeInfoAdapter
@Override
public void onInteraction(final View view, final NodeInfo nodeItem) {
Timber.d("onInteraction");
EditDialog diag = createEditDialog(nodeItem);
if (diag != null) {
diag.show();
}
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.fab:
EditDialog diag = createEditDialog(null);
if (diag != null) {
diag.show();
}
break;
}
}
private class AsyncFindNodes extends AsyncTask<Void, NodeInfo, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
filterFavourites();
nodesAdapter.setNodes(null);
nodesAdapter.allowClick(false);
tvPull.setText(getString(R.string.node_scanning));
}
@Override
protected Boolean doInBackground(Void... params) {
Timber.d("scanning");
Set<NodeInfo> seedList = new HashSet<>();
seedList.addAll(nodeList);
nodeList.clear();
Timber.d("seed %d", seedList.size());
Dispatcher d = new Dispatcher(new Dispatcher.Listener() {
@Override
public void onGet(NodeInfo info) {
publishProgress(info);
}
});
d.seedPeers(seedList);
d.awaitTermination(NODES_TO_FIND);
// we didn't find enough because we didn't ask around enough? ask more!
if ((d.getRpcNodes().size() < NODES_TO_FIND) &&
(d.getPeerCount() < NODES_TO_FIND + seedList.size())) {
// try again
publishProgress((NodeInfo[]) null);
d = new Dispatcher(new Dispatcher.Listener() {
@Override
public void onGet(NodeInfo info) {
publishProgress(info);
}
});
// also seed with monero seed nodes (see p2p/net_node.inl:410 in monero src)
seedList.add(new NodeInfo(new InetSocketAddress("107.152.130.98", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("212.83.175.67", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("5.9.100.248", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("163.172.182.165", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("161.67.132.39", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("198.74.231.92", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("195.154.123.123", 18080)));
seedList.add(new NodeInfo(new InetSocketAddress("212.83.172.165", 18080)));
d.seedPeers(seedList);
d.awaitTermination(NODES_TO_FIND);
}
// final (filtered) result
nodeList.addAll(d.getRpcNodes());
return true;
}
@Override
protected void onProgressUpdate(NodeInfo... values) {
Timber.d("onProgressUpdate");
if (!isCancelled())
if (values != null)
nodesAdapter.addNode(values[0]);
else
nodesAdapter.setNodes(null);
}
@Override
protected void onPostExecute(Boolean result) {
Timber.d("done scanning");
complete();
}
@Override
protected void onCancelled(Boolean result) {
Timber.d("cancelled scanning");
complete();
}
private void complete() {
asyncFindNodes = null;
if (!isAdded()) return;
//if (isCancelled()) return;
tvPull.setText(getString(R.string.node_pull_hint));
pullToRefresh.setRefreshing(false);
nodesAdapter.setNodes(nodeList);
nodesAdapter.allowClick(true);
updateRefreshElements();
}
}
@Override
public void onDetach() {
Timber.d("detached");
super.onDetach();
}
private EditDialog editDialog = null; // for preventing opening of multiple dialogs
private EditDialog createEditDialog(final NodeInfo nodeInfo) {
if (editDialog != null) return null; // we are already open
editDialog = new EditDialog(nodeInfo);
return editDialog;
}
class EditDialog {
final NodeInfo nodeInfo;
final NodeInfo nodeBackup;
private boolean applyChanges() {
nodeInfo.clear();
showTestResult();
final String portString = etNodePort.getEditText().getText().toString().trim();
int port;
if (portString.isEmpty()) {
port = Node.getDefaultRpcPort();
} else {
try {
port = Integer.parseInt(portString);
} catch (NumberFormatException ex) {
etNodePort.setError(getString(R.string.node_port_numeric));
return false;
}
}
etNodePort.setError(null);
if ((port <= 0) || (port > 65535)) {
etNodePort.setError(getString(R.string.node_port_range));
return false;
}
final String host = etNodeHost.getEditText().getText().toString().trim();
if (host.isEmpty()) {
etNodeHost.setError(getString(R.string.node_host_empty));
return false;
}
final boolean setHostSuccess = Helper.runWithNetwork(new Helper.Action() {
@Override
public boolean run() {
try {
nodeInfo.setHost(host);
return true;
} catch (UnknownHostException ex) {
etNodeHost.setError(getString(R.string.node_host_unresolved));
return false;
}
}
});
if (!setHostSuccess) {
etNodeHost.setError(getString(R.string.node_host_unresolved));
return false;
}
etNodeHost.setError(null);
nodeInfo.setRpcPort(port);
// setName() may trigger reverse DNS
Helper.runWithNetwork(new Helper.Action() {
@Override
public boolean run() {
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
return true;
}
});
nodeInfo.setUsername(etNodeUser.getEditText().getText().toString().trim());
nodeInfo.setPassword(etNodePass.getEditText().getText().toString()); // no trim for pw
return true;
}
private boolean shutdown = false;
private void apply() {
if (applyChanges()) {
closeDialog();
if (nodeBackup == null) { // this is a (FAB) new node
nodeInfo.setFavourite(true);
nodeList.add(nodeInfo);
}
shutdown = true;
new AsyncTestNode().execute();
}
}
private void closeDialog() {
if (editDialog == null) throw new IllegalStateException();
Helper.hideKeyboardAlways(getActivity());
editDialog.dismiss();
editDialog = null;
NodeFragment.this.editDialog = null;
}
private void undoChanges() {
if (nodeBackup != null)
nodeInfo.overwriteWith(nodeBackup);
}
private void show() {
editDialog.show();
}
private void test() {
if (applyChanges())
new AsyncTestNode().execute();
}
private void showKeyboard() {
Helper.showKeyboard(editDialog);
}
AlertDialog editDialog = null;
TextInputLayout etNodeName;
TextInputLayout etNodeHost;
TextInputLayout etNodePort;
TextInputLayout etNodeUser;
TextInputLayout etNodePass;
TextView tvResult;
void showTestResult() {
if (nodeInfo.isSuccessful()) {
tvResult.setText(getString(R.string.node_result,
FORMATTER.format(nodeInfo.getHeight()), nodeInfo.getMajorVersion(),
nodeInfo.getResponseTime(), nodeInfo.getHostAddress()));
} else {
tvResult.setText(NodeInfoAdapter.getResponseErrorText(getActivity(), nodeInfo.getResponseCode()));
}
}
EditDialog(final NodeInfo nodeInfo) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
LayoutInflater li = LayoutInflater.from(alertDialogBuilder.getContext());
View promptsView = li.inflate(R.layout.prompt_editnode, null);
alertDialogBuilder.setView(promptsView);
etNodeName = promptsView.findViewById(R.id.etNodeName);
etNodeHost = promptsView.findViewById(R.id.etNodeHost);
etNodePort = promptsView.findViewById(R.id.etNodePort);
etNodeUser = promptsView.findViewById(R.id.etNodeUser);
etNodePass = promptsView.findViewById(R.id.etNodePass);
tvResult = promptsView.findViewById(R.id.tvResult);
if (nodeInfo != null) {
this.nodeInfo = nodeInfo;
nodeBackup = new NodeInfo(nodeInfo);
etNodeName.getEditText().setText(nodeInfo.getName());
etNodeHost.getEditText().setText(nodeInfo.getHost());
etNodePort.getEditText().setText(Integer.toString(nodeInfo.getRpcPort()));
etNodeUser.getEditText().setText(nodeInfo.getUsername());
etNodePass.getEditText().setText(nodeInfo.getPassword());
showTestResult();
} else {
this.nodeInfo = new NodeInfo();
nodeBackup = null;
}
// set dialog message
alertDialogBuilder
.setCancelable(false)
.setPositiveButton(getString(R.string.label_ok), null)
.setNeutralButton(getString(R.string.label_test), null)
.setNegativeButton(getString(R.string.label_cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
undoChanges();
closeDialog();
nodesAdapter.dataSetChanged(); // to refresh test results
}
});
editDialog = alertDialogBuilder.create();
// these need to be here, since we don't always close the dialog
editDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(final DialogInterface dialog) {
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
test();
}
});
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
apply();
}
});
}
});
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
test();
return true;
}
return false;
}
});
}
private class AsyncTestNode extends AsyncTask<Void, Void, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
nodeInfo.clear();
tvResult.setText(getString(R.string.node_testing, nodeInfo.getHostAddress()));
}
@Override
protected Boolean doInBackground(Void... params) {
nodeInfo.testRpcService();
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (editDialog != null) {
showTestResult();
}
if (shutdown) {
if (nodeBackup == null) {
nodesAdapter.addNode(nodeInfo);
} else {
nodesAdapter.dataSetChanged();
}
}
}
}
}
}

@ -92,17 +92,17 @@ public class ReceiveFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_receive, container, false);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel);
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
etNotes = (TextInputLayout) view.findViewById(R.id.etNotes);
evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
qrCode = (ImageView) view.findViewById(R.id.qrCode);
tvQrCode = (TextView) view.findViewById(R.id.tvQrCode);
qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull);
etDummy = (EditText) view.findViewById(R.id.etDummy);
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
bSubaddress = (Button) view.findViewById(R.id.bSubaddress);
pbProgress = view.findViewById(R.id.pbProgress);
tvAddressLabel = view.findViewById(R.id.tvAddressLabel);
tvAddress = view.findViewById(R.id.tvAddress);
etNotes = view.findViewById(R.id.etNotes);
evAmount = view.findViewById(R.id.evAmount);
qrCode = view.findViewById(R.id.qrCode);
tvQrCode = view.findViewById(R.id.tvQrCode);
qrCodeFull = view.findViewById(R.id.qrCodeFull);
etDummy = view.findViewById(R.id.etDummy);
bCopyAddress = view.findViewById(R.id.bCopyAddress);
bSubaddress = view.findViewById(R.id.bSubaddress);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
@ -438,7 +438,7 @@ public class ReceiveFragment extends Fragment {
Bitmap logoBitmap = Bitmap.createBitmap(qrWidth, qrHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(logoBitmap);
canvas.drawBitmap(qrBitmap, 0, 0, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.save();
// figure out how to scale the logo
float scaleSize = 1.0f;
while ((logoWidth / scaleSize) > (qrWidth / 5) || (logoHeight / scaleSize) > (qrHeight / 5)) {
@ -482,7 +482,6 @@ public class ReceiveFragment extends Fragment {
if (context instanceof GenerateReviewFragment.ProgressListener) {
this.progressCallback = (GenerateReviewFragment.ProgressListener) context;
}
}
@Override

@ -31,7 +31,7 @@ public abstract class SecureActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
// set FLAG_SECURE to prevent screenshots in Release Mode
if (!BuildConfig.DEBUG) {
if (!BuildConfig.DEBUG && !BuildConfig.FLAVOR_type.equals("alpha")) {
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
}
}

@ -85,22 +85,22 @@ public class TxFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
cvXmrTo = view.findViewById(R.id.cvXmrTo);
tvTxXmrToKey = (TextView) view.findViewById(R.id.tvTxXmrToKey);
tvDestinationBtc = (TextView) view.findViewById(R.id.tvDestinationBtc);
tvTxAmountBtc = (TextView) view.findViewById(R.id.tvTxAmountBtc);
tvAccount = (TextView) view.findViewById(R.id.tvAccount);
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
tvDestination = (TextView) view.findViewById(R.id.tvDestination);
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
tvTxBlockheight = (TextView) view.findViewById(R.id.tvTxBlockheight);
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
tvTxTransfers = (TextView) view.findViewById(R.id.tvTxTransfers);
etTxNotes = (TextView) view.findViewById(R.id.etTxNotes);
bTxNotes = (Button) view.findViewById(R.id.bTxNotes);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
tvAccount = view.findViewById(R.id.tvAccount);
tvTxTimestamp = view.findViewById(R.id.tvTxTimestamp);
tvTxId = view.findViewById(R.id.tvTxId);
tvTxKey = view.findViewById(R.id.tvTxKey);
tvDestination = view.findViewById(R.id.tvDestination);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
etTxNotes = view.findViewById(R.id.etTxNotes);
bTxNotes = view.findViewById(R.id.bTxNotes);
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);

@ -349,7 +349,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
}
setContentView(R.layout.activity_wallet);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
@ -378,13 +378,13 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
}
});
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer = findViewById(R.id.drawer_layout);
drawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, 0, 0);
drawer.addDrawerListener(drawerToggle);
drawerToggle.syncState();
setDrawerEnabled(false); // disable until synced
accountsView = (NavigationView) findViewById(R.id.accounts_nav);
accountsView = findViewById(R.id.accounts_nav);
accountsView.setNavigationItemSelectedListener(this);
showNet();
@ -1074,7 +1074,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
void updateAccountsHeader() {
final Wallet wallet = getWallet();
final TextView tvName = (TextView) accountsView.getHeaderView(0).findViewById(R.id.tvName);
final TextView tvName = accountsView.getHeaderView(0).findViewById(R.id.tvName);
tvName.setText(wallet.getName());
}
@ -1115,8 +1115,8 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setView(promptsView);
final EditText etRename = (EditText) promptsView.findViewById(R.id.etRename);
final TextView tvRenameLabel = (TextView) promptsView.findViewById(R.id.tvRenameLabel);
final EditText etRename = promptsView.findViewById(R.id.etRename);
final TextView tvRenameLabel = promptsView.findViewById(R.id.tvRenameLabel);
final Wallet wallet = getWallet();
tvRenameLabel.setText(getString(R.string.prompt_rename, wallet.getAccountLabel()));

@ -21,6 +21,7 @@ import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.util.LocaleHelper;
import timber.log.Timber;
@ -46,4 +47,17 @@ public class XmrWalletApplication extends Application {
LocaleHelper.updateSystemDefaultLocale(configuration.locale);
LocaleHelper.setLocale(XmrWalletApplication.this, LocaleHelper.getLocale(XmrWalletApplication.this));
}
static public NetworkType getNetworkType() {
switch (BuildConfig.FLAVOR_net) {
case "mainnet":
return NetworkType.NetworkType_Mainnet;
case "stagenet":
return NetworkType.NetworkType_Stagenet;
case "testnet":
return NetworkType.NetworkType_Testnet;
default:
throw new IllegalStateException("unknown net flavor " + BuildConfig.FLAVOR_net);
}
}
}

@ -0,0 +1,333 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import timber.log.Timber;
public class Node {
static public final String MAINNET = "mainnet";
static public final String STAGENET = "stagenet";
static public final String TESTNET = "testnet";
private String name = null;
final private NetworkType networkType;
InetAddress hostAddress;
private String host;
int rpcPort = 0;
private int levinPort = 0;
private String username = "";
private String password = "";
private boolean favourite = false;
@Override
public int hashCode() {
return hostAddress.hashCode();
}
// Nodes are equal if they are the same host address & are on the same network
@Override
public boolean equals(Object other) {
if (!(other instanceof Node)) return false;
final Node anotherNode = (Node) other;
return (hostAddress.equals(anotherNode.hostAddress) && (networkType == anotherNode.networkType));
}
static public Node fromString(String nodeString) {
try {
return new Node(nodeString);
} catch (IllegalArgumentException ex) {
Timber.w(ex);
return null;
}
}
Node(String nodeString) {
if ((nodeString == null) || nodeString.isEmpty())
throw new IllegalArgumentException("daemon is empty");
String daemonAddress;
String a[] = nodeString.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
username = "";
password = "";
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
username = userPassword[0];
if (!username.isEmpty()) {
password = userPassword[1];
} else {
password = "";
}
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String daParts[] = daemonAddress.split("/");
if ((daParts.length > 3) || (daParts.length < 1))
throw new IllegalArgumentException("Too many '/' or too few");
daemonAddress = daParts[0];
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
String host = da[0];
if (daParts.length == 1) {
networkType = NetworkType.NetworkType_Mainnet;
} else {
switch (daParts[1]) {
case MAINNET:
networkType = NetworkType.NetworkType_Mainnet;
break;
case STAGENET:
networkType = NetworkType.NetworkType_Stagenet;
break;
case TESTNET:
networkType = NetworkType.NetworkType_Testnet;
break;
default:
throw new IllegalArgumentException("invalid net: " + daParts[1]);
}
}
if (networkType != WalletManager.getInstance().getNetworkType())
throw new IllegalArgumentException("wrong net: " + networkType);
String name = host;
if (daParts.length == 3) {
try {
name = URLDecoder.decode(daParts[2], "UTF-8");
} catch (UnsupportedEncodingException ex) {
Timber.w(ex); // if we can't encode it, we don't use it
}
}
this.name = name;
int port;
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
port = getDefaultRpcPort();
}
try {
setHost(host);
} catch (UnknownHostException ex) {
throw new IllegalArgumentException("cannot resolve host " + host);
}
this.rpcPort = port;
this.levinPort = getDefaultLevinPort();
}
public String toNodeString() {
return toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (!username.isEmpty() && !password.isEmpty()) {
sb.append(username).append(":").append(password).append("@");
}
sb.append(host).append(":").append(rpcPort);
sb.append("/");
switch (networkType) {
case NetworkType_Mainnet:
sb.append(MAINNET);
break;
case NetworkType_Stagenet:
sb.append(STAGENET);
break;
case NetworkType_Testnet:
sb.append(TESTNET);
break;
}
if (name != null)
try {
sb.append("/").append(URLEncoder.encode(name, "UTF-8"));
} catch (UnsupportedEncodingException ex) {
Timber.w(ex); // if we can't encode it, we don't store it
}
return sb.toString();
}
public Node() {
this.networkType = WalletManager.getInstance().getNetworkType();
}
// constructor used for created nodes from retrieved peer lists
public Node(InetSocketAddress socketAddress) {
this();
this.hostAddress = socketAddress.getAddress();
this.host = socketAddress.getHostString();
this.rpcPort = 0; // unknown
this.levinPort = socketAddress.getPort();
this.username = "";
this.password = "";
//this.name = socketAddress.getHostName(); // triggers DNS so we don't do it by default
}
public String getAddress() {
return getHostAddress() + ":" + rpcPort;
}
public String getHostAddress() {
return hostAddress.getHostAddress();
}
public String getHost() {
return host;
}
public int getRpcPort() {
return rpcPort;
}
public void setHost(String host) throws UnknownHostException {
if ((host == null) || (host.isEmpty()))
throw new UnknownHostException("loopback not supported (yet?)");
this.host = host;
this.hostAddress = InetAddress.getByName(host);
}
public void setUsername(String user) {
username = user;
}
public void setPassword(String pass) {
password = pass;
}
public void setRpcPort(int port) {
this.rpcPort = port;
}
public void setName() {
if (name == null)
this.name = hostAddress.getHostName();
}
public void setName(String name) {
if ((name == null) || (name.isEmpty()))
this.name = hostAddress.getHostName();
else
this.name = name;
}
public String getName() {
return name;
}
public NetworkType getNetworkType() {
return networkType;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public boolean isFavourite() {
return favourite;
}
public void setFavourite(boolean favourite) {
this.favourite = favourite;
}
public void toggleFavourite() {
favourite = !favourite;
}
public Node(Node anotherNode) {
networkType = anotherNode.networkType;
overwriteWith(anotherNode);
}
public void overwriteWith(Node anotherNode) {
if (networkType != anotherNode.networkType)
throw new IllegalStateException("network types do not match");
name = anotherNode.name;
hostAddress = anotherNode.hostAddress;
host = anotherNode.host;
rpcPort = anotherNode.rpcPort;
levinPort = anotherNode.levinPort;
username = anotherNode.username;
password = anotherNode.password;
favourite = anotherNode.favourite;
}
static private int DEFAULT_LEVIN_PORT = 0;
// every node knows its network, but they are all the same
static public int getDefaultLevinPort() {
if (DEFAULT_LEVIN_PORT > 0) return DEFAULT_LEVIN_PORT;
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
DEFAULT_LEVIN_PORT = 18080;
break;
case NetworkType_Testnet:
DEFAULT_LEVIN_PORT = 28080;
break;
case NetworkType_Stagenet:
DEFAULT_LEVIN_PORT = 38080;
break;
default:
throw new IllegalStateException("unsupported net " + WalletManager.getInstance().getNetworkType());
}
return DEFAULT_LEVIN_PORT;
}
static private int DEFAULT_RPC_PORT = 0;
// every node knows its network, but they are all the same
static public int getDefaultRpcPort() {
if (DEFAULT_RPC_PORT > 0) return DEFAULT_RPC_PORT;
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
DEFAULT_RPC_PORT = 18081;
break;
case NetworkType_Testnet:
DEFAULT_RPC_PORT = 28081;
break;
case NetworkType_Stagenet:
DEFAULT_RPC_PORT = 38081;
break;
default:
throw new IllegalStateException("unsupported net " + WalletManager.getInstance().getNetworkType());
}
return DEFAULT_RPC_PORT;
}
}

@ -0,0 +1,266 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import com.burgstaller.okhttp.AuthenticationCacheInterceptor;
import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
import com.burgstaller.okhttp.digest.CachingAuthenticator;
import com.burgstaller.okhttp.digest.Credentials;
import com.burgstaller.okhttp.digest.DigestAuthenticator;
import com.m2049r.levin.scanner.Dispatcher;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import timber.log.Timber;
public class NodeInfo extends Node {
final static public int MIN_MAJOR_VERSION = 9;
private long height = 0;
private long timestamp = 0;
private int majorVersion = 0;
private double responseTime = Double.MAX_VALUE;
private int responseCode = 0;
public void clear() {
height = 0;
majorVersion = 0;
responseTime = Double.MAX_VALUE;
responseCode = 0;
timestamp = 0;
}
static public NodeInfo fromString(String nodeString) {
try {
return new NodeInfo(nodeString);
} catch (IllegalArgumentException ex) {
Timber.w(ex);
return null;
}
}
public NodeInfo(NodeInfo anotherNode) {
super(anotherNode);
overwriteWith(anotherNode);
}
private SocketAddress levinSocketAddress = null;
synchronized public SocketAddress getLevinSocketAddress() {
if (levinSocketAddress == null) {
// use default peer port if not set - very few peers use nonstandard port
levinSocketAddress = new InetSocketAddress(hostAddress, getDefaultLevinPort());
}
return levinSocketAddress;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object other) {
return super.equals(other);
}
public NodeInfo(String nodeString) {
super(nodeString);
}
public NodeInfo(InetSocketAddress socketAddress) {
super(socketAddress);
}
public NodeInfo() {
super();
}
public NodeInfo(InetSocketAddress peerAddress, long height, int majorVersion, double respTime) {
super(peerAddress);
this.height = height;
this.majorVersion = majorVersion;
this.responseTime = respTime;
}
public long getHeight() {
return height;
}
public long getTimestamp() {
return timestamp;
}
public int getMajorVersion() {
return majorVersion;
}
public double getResponseTime() {
return responseTime;
}
public int getResponseCode() {
return responseCode;
}
public boolean isSuccessful() {
return (responseCode >= 200) && (responseCode < 300);
}
public boolean isUnauthorized() {
return responseCode == HttpURLConnection.HTTP_UNAUTHORIZED;
}
public boolean isValid() {
return isSuccessful() && (majorVersion >= MIN_MAJOR_VERSION) && (responseTime < Double.MAX_VALUE);
}
static public Comparator<NodeInfo> BestNodeComparator = new Comparator<NodeInfo>() {
@Override
public int compare(NodeInfo o1, NodeInfo o2) {
if (o1.isValid()) {
if (o2.isValid()) { // both are valid
// higher node wins
int heightDiff = (int) (o2.height - o1.height);
if (Math.abs(heightDiff) > Dispatcher.HEIGHT_WINDOW)
return heightDiff;
// if they are (nearly) equal, faster node wins
return (int) Math.signum(o1.responseTime - o2.responseTime);
} else {
return -1;
}
} else {
return 1;
}
}
};
public void overwriteWith(NodeInfo anotherNode) {
super.overwriteWith(anotherNode);
height = anotherNode.height;
timestamp = anotherNode.timestamp;
majorVersion = anotherNode.majorVersion;
responseTime = anotherNode.responseTime;
responseCode = anotherNode.responseCode;
}
public String toNodeString() {
return super.toString();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append("?rc=").append(responseCode);
sb.append("?v=").append(majorVersion);
sb.append("&h=").append(height);
sb.append("&ts=").append(timestamp);
if (responseTime < Double.MAX_VALUE) {
sb.append("&t=").append(responseTime).append("ms");
}
return sb.toString();
}
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
public static final double PING_GOOD = HTTP_TIMEOUT / 3; //ms
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
public static final double PING_BAD = HTTP_TIMEOUT;
public boolean testRpcService() {
return testRpcService(rpcPort);
}
private boolean testRpcService(int port) {
clear();
try {
OkHttpClient client = OkHttpHelper.getEagerClient();
if (!getUsername().isEmpty()) {
final DigestAuthenticator authenticator =
new DigestAuthenticator(new Credentials(getUsername(), getPassword()));
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
client = client.newBuilder()
.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
.addInterceptor(new AuthenticationCacheInterceptor(authCache))
.build();
}
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(getHostAddress())
.port(port)
.addPathSegment("json_rpc")
.build();
final RequestBody reqBody = RequestBody
.create(MediaType.parse("application/json"),
"{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}");
Request request = OkHttpHelper.getPostRequest(url, reqBody);
long ta = System.nanoTime();
try (Response response = client.newCall(request).execute()) {
responseTime = (System.nanoTime() - ta) / 1000000.0;
responseCode = response.code();
if (response.isSuccessful()) {
ResponseBody respBody = response.body(); // closed through Response object
if ((respBody != null) && (respBody.contentLength() < 1000)) { // sanity check
final JSONObject json = new JSONObject(
respBody.string());
final JSONObject header = json.getJSONObject(
"result").getJSONObject("block_header");
height = header.getLong("height");
timestamp = header.getLong("timestamp");
majorVersion = header.getInt("major_version");
return true; // success
}
}
}
} catch (IOException | JSONException ex) {
// failure
Timber.d(ex.getMessage());
}
return false;
}
static final private int[] TEST_PORTS = {18089}; // check only opt-in port
public boolean findRpcService() {
// if already have an rpcPort, use that
if (rpcPort > 0) return testRpcService(rpcPort);
// otherwise try to find one
for (int port : TEST_PORTS) {
if (testRpcService(port)) { // found a service
this.rpcPort = port;
return true;
}
}
return false;
}
}

@ -1,112 +0,0 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.model.NetworkType;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class WalletNode {
private final String name;
private final String host;
private final int port;
private final String user;
private final String password;
private final NetworkType networkType;
public WalletNode(String walletName, String daemon, NetworkType networkType) {
if ((daemon == null) || daemon.isEmpty())
throw new IllegalArgumentException("daemon is empty");
this.name = walletName;
String daemonAddress;
String a[] = daemon.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
user = "";
password = "";
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
user = userPassword[0];
if (!user.isEmpty()) {
password = userPassword[1];
} else {
password = "";
}
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
host = da[0];
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
switch (networkType) {
case NetworkType_Mainnet:
port = 18081;
break;
case NetworkType_Testnet:
port = 28081;
break;
case NetworkType_Stagenet:
port = 38081;
break;
default:
port = 0;
}
}
this.networkType = networkType;
}
public String getName() {
return name;
}
public NetworkType getNetworkType() {
return networkType;
}
public String getAddress() {
return host + ":" + port;
}
public String getUsername() {
return user;
}
public String getPassword() {
return password;
}
public SocketAddress getSocketAddress() {
return new InetSocketAddress(host, port);
}
public boolean isValid() {
return !host.isEmpty();
}
}

@ -57,10 +57,10 @@ public class ProgressDialog extends AlertDialog {
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);
tvMessage = 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);
pbBar = view.findViewById(R.id.pbBar);
tvProgress = view.findViewById(R.id.tvProgress);
setView(view);
//setTitle("blabla");
//super.setMessage("bubbu");

@ -106,10 +106,10 @@ public class SendAddressWizardFragment extends SendWizardFragment {
tvPaymentIdIntegrated = view.findViewById(R.id.tvPaymentIdIntegrated);
llPaymentId = view.findViewById(R.id.llPaymentId);
llXmrTo = view.findViewById(R.id.llXmrTo);
tvXmrTo = (TextView) view.findViewById(R.id.tvXmrTo);
tvXmrTo = view.findViewById(R.id.tvXmrTo);
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto)));
etAddress = (TextInputLayout) view.findViewById(R.id.etAddress);
etAddress = view.findViewById(R.id.etAddress);
etAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@ -168,7 +168,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
}
});
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
etPaymentId = view.findViewById(R.id.etPaymentId);
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@ -197,7 +197,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
}
});
bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
bPaymentId = view.findViewById(R.id.bPaymentId);
bPaymentId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -205,7 +205,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
}
});
etNotes = (TextInputLayout) view.findViewById(R.id.etNotes);
etNotes = view.findViewById(R.id.etNotes);
etNotes.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT);
etNotes.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@ -219,7 +219,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
}
});
cvScan = (CardView) view.findViewById(R.id.bScan);
cvScan = view.findViewById(R.id.bScan);
cvScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -228,7 +228,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
});
etDummy = (EditText) view.findViewById(R.id.etDummy);
etDummy = view.findViewById(R.id.etDummy);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());

@ -74,9 +74,9 @@ public class SendAmountWizardFragment extends SendWizardFragment {
View view = inflater.inflate(R.layout.fragment_send_amount, container, false);
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
tvFunds = view.findViewById(R.id.tvFunds);
evAmount = (ExchangeTextView) view.findViewById(R.id.evAmount);
evAmount = view.findViewById(R.id.evAmount);
((NumberPadView) view.findViewById(R.id.numberPad)).setListener(evAmount);
rlSweep = view.findViewById(R.id.rlSweep);
@ -88,7 +88,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
}
});
ibSweep = (ImageButton) view.findViewById(R.id.ibSweep);
ibSweep = view.findViewById(R.id.ibSweep);
ibSweep.setOnClickListener(new View.OnClickListener() {
@Override

@ -30,7 +30,7 @@ import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.widget.ExchangeBtcTextView;
import com.m2049r.xmrwallet.widget.NumberPadView;
import com.m2049r.xmrwallet.widget.SendProgressView;
@ -44,7 +44,6 @@ import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
import java.text.NumberFormat;
import java.util.Locale;
import okhttp3.HttpUrl;
import timber.log.Timber;
public class SendBtcAmountWizardFragment extends SendWizardFragment {
@ -80,15 +79,15 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
View view = inflater.inflate(R.layout.fragment_send_btc_amount, container, false);
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
tvFunds = view.findViewById(R.id.tvFunds);
evParams = (SendProgressView) view.findViewById(R.id.evXmrToParms);
evParams = view.findViewById(R.id.evXmrToParms);
llXmrToParms = view.findViewById(R.id.llXmrToParms);
tvXmrToParms = (TextView) view.findViewById(R.id.tvXmrToParms);
tvXmrToParms = view.findViewById(R.id.tvXmrToParms);
evAmount = (ExchangeBtcTextView) view.findViewById(R.id.evAmount);
numberPad = (NumberPadView) view.findViewById(R.id.numberPad);
evAmount = view.findViewById(R.id.evAmount);
numberPad = view.findViewById(R.id.numberPad);
numberPad.setListener(evAmount);
Helper.hideKeyboard(getActivity());
@ -263,7 +262,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
}
}

@ -38,7 +38,7 @@ import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.widget.SendProgressView;
import com.m2049r.xmrwallet.xmrto.XmrToError;
import com.m2049r.xmrwallet.xmrto.XmrToException;
@ -51,7 +51,6 @@ import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
import java.text.NumberFormat;
import java.util.Locale;
import okhttp3.HttpUrl;
import timber.log.Timber;
public class SendBtcConfirmWizardFragment extends SendWizardFragment implements SendConfirm {
@ -95,21 +94,21 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
View view = inflater.inflate(
R.layout.fragment_send_btc_confirm, container, false);
tvTxBtcAddress = (TextView) view.findViewById(R.id.tvTxBtcAddress);
tvTxBtcAmount = ((TextView) view.findViewById(R.id.tvTxBtcAmount));
tvTxBtcRate = (TextView) view.findViewById(R.id.tvTxBtcRate);
tvTxXmrToKey = (TextView) view.findViewById(R.id.tvTxXmrToKey);
tvTxBtcAddress = view.findViewById(R.id.tvTxBtcAddress);
tvTxBtcAmount = view.findViewById(R.id.tvTxBtcAmount);
tvTxBtcRate = view.findViewById(R.id.tvTxBtcRate);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
tvTxTotal = (TextView) view.findViewById(R.id.tvTxTotal);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTotal = view.findViewById(R.id.tvTxTotal);
llStageA = view.findViewById(R.id.llStageA);
evStageA = (SendProgressView) view.findViewById(R.id.evStageA);
evStageA = view.findViewById(R.id.evStageA);
llStageB = view.findViewById(R.id.llStageB);
evStageB = (SendProgressView) view.findViewById(R.id.evStageB);
evStageB = view.findViewById(R.id.evStageB);
llStageC = view.findViewById(R.id.llStageC);
evStageC = (SendProgressView) view.findViewById(R.id.evStageC);
evStageC = view.findViewById(R.id.evStageC);
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
@Override
@ -122,7 +121,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
llConfirmSend = view.findViewById(R.id.llConfirmSend);
pbProgressSend = view.findViewById(R.id.pbProgressSend);
bSend = (Button) view.findViewById(R.id.bSend);
bSend = view.findViewById(R.id.bSend);
bSend.setEnabled(false);
bSend.setOnClickListener(new View.OnClickListener() {
@ -350,7 +349,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
android.app.AlertDialog.Builder alertDialogBuilder = new android.app.AlertDialog.Builder(activity);
alertDialogBuilder.setView(promptsView);
final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword);
final TextInputLayout etPassword = promptsView.findViewById(R.id.etPassword);
etPassword.setHint(getString(R.string.prompt_send_password));
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
@ -671,7 +670,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
}
}

@ -30,7 +30,7 @@ import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.xmrto.XmrToException;
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
@ -40,7 +40,6 @@ import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
import java.text.NumberFormat;
import java.util.Locale;
import okhttp3.HttpUrl;
import timber.log.Timber;
public class SendBtcSuccessWizardFragment extends SendWizardFragment {
@ -80,7 +79,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
View view = inflater.inflate(
R.layout.fragment_send_btc_success, container, false);
bCopyTxId = (ImageButton) view.findViewById(R.id.bCopyTxId);
bCopyTxId = view.findViewById(R.id.bCopyTxId);
bCopyTxId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -89,27 +88,26 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
}
});
tvXmrToAmount = (TextView) view.findViewById(R.id.tvXmrToAmount);
tvXmrToStatus = (TextView) view.findViewById(R.id.tvXmrToStatus);
ivXmrToStatus = (ImageView) view.findViewById(R.id.ivXmrToStatus);
ivXmrToStatusBig = (ImageView) view.findViewById(R.id.ivXmrToStatusBig);
tvXmrToAmount = view.findViewById(R.id.tvXmrToAmount);
tvXmrToStatus = view.findViewById(R.id.tvXmrToStatus);
ivXmrToStatus = view.findViewById(R.id.ivXmrToStatus);
ivXmrToStatusBig = view.findViewById(R.id.ivXmrToStatusBig);
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
tvTxAddress = (TextView) view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = ((TextView) view.findViewById(R.id.tvTxAmount));
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
tvTxId = view.findViewById(R.id.tvTxId);
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
pbXmrto = (ProgressBar) view.findViewById(R.id.pbXmrto);
pbXmrto = view.findViewById(R.id.pbXmrto);
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
tvTxXmrToKey = (TextView) view.findViewById(R.id.tvTxXmrToKey);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
;
}
});
@ -257,7 +255,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
}
}

@ -87,12 +87,12 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
View view = inflater.inflate(
R.layout.fragment_send_confirm, container, false);
tvTxAddress = (TextView) view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
tvTxNotes = (TextView) view.findViewById(R.id.tvTxNotes);
tvTxAmount = ((TextView) view.findViewById(R.id.tvTxAmount));
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
tvTxTotal = (TextView) view.findViewById(R.id.tvTxTotal);
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxNotes = view.findViewById(R.id.tvTxNotes);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTotal = view.findViewById(R.id.tvTxTotal);
llProgress = view.findViewById(R.id.llProgress);
pbProgressSend = view.findViewById(R.id.pbProgressSend);
@ -231,7 +231,7 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
android.app.AlertDialog.Builder alertDialogBuilder = new android.app.AlertDialog.Builder(activity);
alertDialogBuilder.setView(promptsView);
final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword);
final TextInputLayout etPassword = promptsView.findViewById(R.id.etPassword);
etPassword.setHint(getString(R.string.prompt_send_password));
etPassword.getEditText().addTextChangedListener(new TextWatcher() {

@ -112,18 +112,18 @@ public class SendFragment extends Fragment
final View view = inflater.inflate(R.layout.fragment_send, container, false);
llNavBar = view.findViewById(R.id.llNavBar);
bDone = (Button) view.findViewById(R.id.bDone);
bDone = view.findViewById(R.id.bDone);
dotBar = (DotBar) view.findViewById(R.id.dotBar);
bPrev = (Button) view.findViewById(R.id.bPrev);
bNext = (Button) view.findViewById(R.id.bNext);
dotBar = view.findViewById(R.id.dotBar);
bPrev = view.findViewById(R.id.bPrev);
bNext = view.findViewById(R.id.bNext);
arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_white_24dp);
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
ViewGroup llNotice = view.findViewById(R.id.llNotice);
Notice.showAll(llNotice, ".*_send");
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
spendViewPager = view.findViewById(R.id.pager);
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
spendViewPager.setOffscreenPageLimit(pagerAdapter.getCount()); // load & keep all pages in cache
spendViewPager.setAdapter(pagerAdapter);
@ -183,7 +183,7 @@ public class SendFragment extends Fragment
updatePosition(0);
etDummy = (EditText) view.findViewById(R.id.etDummy);
etDummy = view.findViewById(R.id.etDummy);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());

@ -72,7 +72,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
View view = inflater.inflate(
R.layout.fragment_send_success, container, false);
bCopyTxId = (ImageButton) view.findViewById(R.id.bCopyTxId);
bCopyTxId = view.findViewById(R.id.bCopyTxId);
bCopyTxId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -81,11 +81,11 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
}
});
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
tvTxAddress = (TextView) view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = ((TextView) view.findViewById(R.id.tvTxAmount));
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
tvTxId = view.findViewById(R.id.tvTxId);
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
return view;
}

@ -0,0 +1,195 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.layout;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.NodeInfo;
import java.net.HttpURLConnection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHolder> {
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public interface OnInteractionListener {
void onInteraction(View view, NodeInfo item);
}
private final List<NodeInfo> nodeItems = new ArrayList<>();
private final OnInteractionListener listener;
private Context context;
public NodeInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context;
this.listener = listener;
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone(); //get the local time zone.
TS_FORMATTER.setTimeZone(tz);
}
@Override
public @NonNull
ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_node, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final @NonNull ViewHolder holder, int position) {
holder.bind(position);
}
@Override
public int getItemCount() {
return nodeItems.size();
}
public void addNode(NodeInfo node) {
if (!nodeItems.contains(node))
nodeItems.add(node);
dataSetChanged(); // in case the nodeinfo has changed
}
public void dataSetChanged() {
Collections.sort(nodeItems, NodeInfo.BestNodeComparator);
notifyDataSetChanged();
}
public void setNodes(Collection<NodeInfo> data) {
nodeItems.clear();
if (data != null) {
for (NodeInfo node : data) {
if (!nodeItems.contains(node))
nodeItems.add(node);
}
}
dataSetChanged();
}
private boolean itemsClickable = true;
public void allowClick(boolean clickable) {
itemsClickable = clickable;
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
final ImageButton ibBookmark;
final TextView tvName;
final TextView tvIp;
final ImageView ivPing;
NodeInfo nodeItem;
ViewHolder(View itemView) {
super(itemView);
ibBookmark = itemView.findViewById(R.id.ibBookmark);
tvName = itemView.findViewById(R.id.tvName);
tvIp = itemView.findViewById(R.id.tvAddress);
ivPing = itemView.findViewById(R.id.ivPing);
ibBookmark.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nodeItem.toggleFavourite();
showStar();
}
});
}
private void showStar() {
if (nodeItem.isFavourite()) {
ibBookmark.setImageResource(R.drawable.ic_bookmark_24dp);
} else {
ibBookmark.setImageResource(R.drawable.ic_bookmark_border_24dp);
}
}
void bind(int position) {
nodeItem = nodeItems.get(position);
tvName.setText(nodeItem.getName());
final String ts = TS_FORMATTER.format(new Date(nodeItem.getTimestamp() * 1000));
ivPing.setImageResource(getPingIcon(nodeItem));
if (nodeItem.isValid()) {
tvIp.setText(context.getString(R.string.node_height, ts));
} else {
tvIp.setText(getResponseErrorText(context, nodeItem.getResponseCode()));
}
itemView.setOnClickListener(this);
itemView.setClickable(itemsClickable);
ibBookmark.setClickable(itemsClickable);
showStar();
}
@Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition(); // gets item position
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
listener.onInteraction(view, nodeItems.get(position));
}
}
}
}
static public int getPingIcon(NodeInfo nodeInfo) {
if (nodeInfo.isUnauthorized()) {
return R.drawable.ic_wifi_lock_black_24dp;
}
if (nodeInfo.isValid()) {
final double ping = nodeInfo.getResponseTime();
if (ping < NodeInfo.PING_GOOD) {
return R.drawable.ic_signal_wifi_4_bar_black_24dp;
} else if (ping < NodeInfo.PING_MEDIUM) {
return R.drawable.ic_signal_wifi_3_bar_black_24dp;
} else if (ping < NodeInfo.PING_BAD) {
return R.drawable.ic_signal_wifi_2_bar_black_24dp;
} else {
return R.drawable.ic_signal_wifi_1_bar_black_24dp;
}
} else {
return R.drawable.ic_signal_wifi_off_black_24dp;
}
}
static public String getResponseErrorText(Context ctx, int responseCode) {
if (responseCode == 0) {
return ctx.getResources().getString(R.string.node_general_error);
} else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
return ctx.getResources().getString(R.string.node_auth_error);
} else {
return ctx.getResources().getString(R.string.node_test_error, responseCode);
}
}
}

@ -112,11 +112,11 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ViewHolder(View itemView) {
super(itemView);
ivTxType = (ImageView) itemView.findViewById(R.id.ivTxType);
tvAmount = (TextView) itemView.findViewById(R.id.tx_amount);
tvFee = (TextView) itemView.findViewById(R.id.tx_fee);
tvPaymentId = (TextView) itemView.findViewById(R.id.tx_paymentid);
tvDateTime = (TextView) itemView.findViewById(R.id.tx_datetime);
ivTxType = itemView.findViewById(R.id.ivTxType);
tvAmount = itemView.findViewById(R.id.tx_amount);
tvFee = itemView.findViewById(R.id.tx_fee);
tvPaymentId = itemView.findViewById(R.id.tx_paymentid);
tvDateTime = itemView.findViewById(R.id.tx_datetime);
}
private String getDateTime(long time) {

@ -107,9 +107,9 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
ViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tvName);
tvAddress = (TextView) itemView.findViewById(R.id.tvAddress);
ibOptions = (ImageButton) itemView.findViewById(R.id.ibOptions);
tvName = itemView.findViewById(R.id.tvName);
tvAddress = itemView.findViewById(R.id.tvAddress);
ibOptions = itemView.findViewById(R.id.ibOptions);
ibOptions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

@ -53,7 +53,7 @@ public enum Instruction {
INS_GET_RESPONSE(0xc0),
INS_UNDEFINED(0xff);;
INS_UNDEFINED(0xff);
public static Instruction fromByte(byte n) {
switch (n & 0xFF) {

@ -126,8 +126,8 @@ public class Ledger {
if (response.length < 2) {
throw new BTChipException("Truncated response");
}
lastSW = ((int) (response[response.length - 2] & 0xff) << 8) |
(int) (response[response.length - 1] & 0xff);
lastSW = ((response[response.length - 2] & 0xff) << 8) |
response[response.length - 1] & 0xff;
byte[] result = new byte[response.length - 2];
System.arraycopy(response, 0, result, 0, response.length - 2);
return result;

@ -64,7 +64,7 @@ public class Wallet {
Device_Undefined,
Device_Software,
Device_Ledger
};
}
public enum Status {
Status_Ok,

@ -16,7 +16,8 @@
package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.WalletNode;
import com.m2049r.xmrwallet.XmrWalletApplication;
import com.m2049r.xmrwallet.data.Node;
import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.util.RestoreHeight;
@ -48,7 +49,23 @@ public class WalletManager {
return WalletManager.Instance;
}
//private Map<String, Wallet> managedWallets;
public String addressPrefix() {
return addressPrefix(getNetworkType());
}
static public String addressPrefix(NetworkType networkType) {
switch (networkType) {
case NetworkType_Testnet:
return "9A-";
case NetworkType_Mainnet:
return "4-";
case NetworkType_Stagenet:
return "5-";
default:
throw new IllegalStateException("Unsupported Network: " + networkType);
}
}
private Wallet managedWallet = null;
public Wallet getWallet() {
@ -252,23 +269,26 @@ public class WalletManager {
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
private String daemonAddress = null;
private NetworkType networkType = null;
private final NetworkType networkType = XmrWalletApplication.getNetworkType();
public NetworkType getNetworkType() {
return networkType;
}
//public void setDaemon(String address, NetworkType networkType, String username, String password) {
public void setDaemon(WalletNode walletNode) {
this.daemonAddress = walletNode.getAddress();
this.networkType = walletNode.getNetworkType();
this.daemonUsername = walletNode.getUsername();
this.daemonPassword = walletNode.getPassword();
setDaemonAddressJ(daemonAddress);
}
public void setNetworkType(NetworkType networkType) {
this.networkType = networkType;
public void setDaemon(Node node) {
if (node != null) {
this.daemonAddress = node.getAddress();
if (networkType != node.getNetworkType())
throw new IllegalArgumentException("network type does not match");
this.daemonUsername = node.getUsername();
this.daemonPassword = node.getPassword();
setDaemonAddressJ(daemonAddress);
} else {
this.daemonAddress = null;
this.daemonUsername = "";
this.daemonPassword = "";
setDaemonAddressJ("");
}
}
public String getDaemonAddress() {

@ -107,7 +107,7 @@ public class ExchangeApiImpl implements ExchangeApi {
final JSONObject metadata = json.getJSONObject("metadata");
if (!metadata.isNull("error")) {
final String errorMsg = metadata.getString("error");
callback.onError(new ExchangeException(response.code(), (String) errorMsg));
callback.onError(new ExchangeException(response.code(), errorMsg));
} else {
final JSONObject jsonResult = json.getJSONObject("data");
reportSuccess(jsonResult, swapAssets, callback);

@ -36,6 +36,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.StrictMode;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
import android.system.ErrnoException;
@ -77,7 +78,7 @@ import timber.log.Timber;
public class Helper {
static private final String FLAVOR_SUFFIX =
(BuildConfig.FLAVOR.equals("prod") ? "" : "." + BuildConfig.FLAVOR)
(BuildConfig.FLAVOR.startsWith("prod") ? "" : "." + BuildConfig.FLAVOR)
+ (BuildConfig.DEBUG ? "-debug" : "");
static public final String CRYPTO = "XMR";
@ -397,10 +398,10 @@ public class Helper {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setView(promptsView);
final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword);
final TextInputLayout etPassword = promptsView.findViewById(R.id.etPassword);
etPassword.setHint(context.getString(R.string.prompt_password, wallet));
final TextView tvOpenPrompt = (TextView) promptsView.findViewById(R.id.tvOpenPrompt);
final TextView tvOpenPrompt = promptsView.findViewById(R.id.tvOpenPrompt);
final Drawable icFingerprint = context.getDrawable(R.drawable.ic_fingerprint);
final Drawable icError = context.getDrawable(R.drawable.ic_error_red_36dp);
final Drawable icInfo = context.getDrawable(R.drawable.ic_info_green_36dp);
@ -598,6 +599,21 @@ public class Helper {
}
static public ExchangeApi getExchangeApi() {
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpHelper.getOkHttpClient());
}
public interface Action {
boolean run();
}
static public boolean runWithNetwork(Action action) {
StrictMode.ThreadPolicy currentPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);
try {
return action.run();
} finally {
StrictMode.setThreadPolicy(currentPolicy);
}
}
}

@ -102,11 +102,7 @@ public class KeyStoreHelper {
if (isArm32 != null) return isArm32;
synchronized (KeyStoreException.class) {
if (isArm32 != null) return isArm32;
if (Build.SUPPORTED_ABIS[0].equals("armeabi-v7a")) {
isArm32 = true;
} else {
isArm32 = false;
}
isArm32 = Build.SUPPORTED_ABIS[0].equals("armeabi-v7a");
return isArm32;
}
}

@ -1,63 +0,0 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NodeList {
private static final int MAX_SIZE = 5;
private List<String> nodes = new ArrayList<>();
public List<String> getNodes() {
return nodes;
}
public void setRecent(String aNode) {
if (aNode.trim().isEmpty()) return;
boolean found = false;
for (int i = 0; i < nodes.size(); i++) {
if (nodes.get(i).equals(aNode)) { // node is already in the list => move it to top
nodes.remove(i);
found = true;
break;
}
}
if (!found) {
if (nodes.size() > MAX_SIZE) {
nodes.remove(nodes.size() - 1); // drop last one
}
}
nodes.add(0, aNode);
}
public NodeList(String aString) {
String[] newNodes = aString.split(";");
nodes.addAll(Arrays.asList(newNodes));
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for (String node : this.nodes) {
sb.append(node).append(";");
}
return sb.toString();
}
}

@ -40,16 +40,16 @@ public class Notice {
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_LEDGER = "notice_ledger_enabled_login";
private static final String NOTICE_SHOW_STREET = "notice_streetmode_login";
private static final String NOTICE_SHOW_NODES = "notice_nodes";
private static void init() {
synchronized (Notice.class) {
if (notices != null) return;
notices = new ArrayList<>();
notices.add(
new Notice(NOTICE_SHOW_STREET,
R.string.info_streetmode_enabled,
R.string.help_wallet,
new Notice(NOTICE_SHOW_NODES,
R.string.info_nodes_enabled,
R.string.help_node,
1)
);
notices.add(
@ -115,7 +115,7 @@ public class Notice {
}
});
ImageButton ib = (ImageButton) ll.findViewById(R.id.ibClose);
ImageButton ib = ll.findViewById(R.id.ibClose);
ib.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

@ -1,34 +0,0 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.util;
import okhttp3.OkHttpClient;
public class OkHttpClientSingleton {
static private OkHttpClient Singleton;
static public final OkHttpClient getOkHttpClient() {
if (Singleton == null) {
synchronized (com.m2049r.xmrwallet.util.OkHttpClientSingleton.class) {
if (Singleton == null) {
Singleton = new OkHttpClient();
}
}
}
return Singleton;
}
}

@ -0,0 +1,72 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.util;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
public class OkHttpHelper {
static private OkHttpClient Singleton;
static public OkHttpClient getOkHttpClient() {
if (Singleton == null) {
synchronized (OkHttpHelper.class) {
if (Singleton == null) {
Singleton = new OkHttpClient();
}
}
}
return Singleton;
}
public static final int HTTP_TIMEOUT = 1000; //ms
static private OkHttpClient EagerSingleton;
static public OkHttpClient getEagerClient() {
if (EagerSingleton == null) {
synchronized (OkHttpHelper.class) {
if (EagerSingleton == null) {
EagerSingleton = new OkHttpClient.Builder()
.connectTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
.readTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
.build();
}
}
}
return EagerSingleton;
}
static final public String USER_AGENT = "Monerujo/1.0";
static public Request getPostRequest(HttpUrl url, RequestBody requestBody) {
return new Request.Builder().url(url).post(requestBody)
.header("User-Agent", USER_AGENT)
.build();
}
static public Request getGetRequest(HttpUrl url) {
return new Request.Builder().url(url).get()
.header("User-Agent", USER_AGENT)
.build();
}
}

@ -131,10 +131,10 @@ public class ExchangeBtcTextView extends LinearLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
tvAmountA = (TextView) findViewById(R.id.tvAmountA);
tvAmountB = (TextView) findViewById(R.id.tvAmountB);
sCurrencyA = (Spinner) findViewById(R.id.sCurrencyA);
sCurrencyB = (Spinner) findViewById(R.id.sCurrencyB);
tvAmountA = findViewById(R.id.tvAmountA);
tvAmountB = findViewById(R.id.tvAmountB);
sCurrencyA = findViewById(R.id.sCurrencyA);
sCurrencyB = findViewById(R.id.sCurrencyB);
ArrayAdapter<String> btcAdapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item,

@ -177,12 +177,12 @@ public class ExchangeTextView extends LinearLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
tvAmountA = (TextView) findViewById(R.id.tvAmountA);
tvAmountB = (TextView) findViewById(R.id.tvAmountB);
sCurrencyA = (Spinner) findViewById(R.id.sCurrencyA);
sCurrencyB = (Spinner) findViewById(R.id.sCurrencyB);
evExchange = (ImageView) findViewById(R.id.evExchange);
pbExchange = (ProgressBar) findViewById(R.id.pbExchange);
tvAmountA = findViewById(R.id.tvAmountA);
tvAmountB = findViewById(R.id.tvAmountB);
sCurrencyA = findViewById(R.id.sCurrencyA);
sCurrencyB = findViewById(R.id.sCurrencyB);
evExchange = findViewById(R.id.evExchange);
pbExchange = findViewById(R.id.pbExchange);
// make progress circle gray
pbExchange.getIndeterminateDrawable().

@ -162,16 +162,16 @@ public class ExchangeView extends LinearLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
etAmount = (TextInputLayout) findViewById(R.id.etAmount);
tvAmountB = (TextView) findViewById(R.id.tvAmountB);
sCurrencyA = (Spinner) findViewById(R.id.sCurrencyA);
etAmount = findViewById(R.id.etAmount);
tvAmountB = findViewById(R.id.tvAmountB);
sCurrencyA = findViewById(R.id.sCurrencyA);
ArrayAdapter adapter = ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner);
adapter.setDropDownViewResource(R.layout.item_spinner_dropdown_item);
sCurrencyA.setAdapter(adapter);
sCurrencyB = (Spinner) findViewById(R.id.sCurrencyB);
sCurrencyB = findViewById(R.id.sCurrencyB);
sCurrencyB.setAdapter(adapter);
evExchange = (ImageView) findViewById(R.id.evExchange);
pbExchange = (ProgressBar) findViewById(R.id.pbExchange);
evExchange = findViewById(R.id.evExchange);
pbExchange = findViewById(R.id.pbExchange);
// make progress circle gray
pbExchange.getIndeterminateDrawable().

@ -57,9 +57,9 @@ public class SendProgressView extends LinearLayout {
super.onFinishInflate();
pbProgress = findViewById(R.id.pbProgress);
llMessage = findViewById(R.id.llMessage);
tvCode = (TextView) findViewById(R.id.tvCode);
tvMessage = (TextView) findViewById(R.id.tvMessage);
tvSolution = (TextView) findViewById(R.id.tvSolution);
tvCode = findViewById(R.id.tvCode);
tvMessage = findViewById(R.id.tvMessage);
tvSolution = findViewById(R.id.tvSolution);
}
public void showProgress(String progressText) {

@ -78,16 +78,16 @@ public class Toolbar extends android.support.v7.widget.Toolbar {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
toolbarImage = (ImageView) findViewById(R.id.toolbarImage);
toolbarImage = findViewById(R.id.toolbarImage);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
// the vector image does not work well for androis < Nougat
toolbarImage.getLayoutParams().width = (int) getResources().getDimension(R.dimen.logo_width);
toolbarImage.setImageResource(R.drawable.logo_horizontol_xmrujo);
}
toolbarTitle = (TextView) findViewById(R.id.toolbarTitle);
toolbarSubtitle = (TextView) findViewById(R.id.toolbarSubtitle);
bCredits = (Button) findViewById(R.id.bCredits);
toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarSubtitle = findViewById(R.id.toolbarSubtitle);
bCredits = findViewById(R.id.bCredits);
bCredits.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (onButtonListener != null) {

@ -17,15 +17,15 @@
package com.m2049r.xmrwallet.xmrto.network;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.xmrto.XmrToError;
import com.m2049r.xmrwallet.xmrto.XmrToException;
import com.m2049r.xmrwallet.xmrto.api.CreateOrder;
import com.m2049r.xmrwallet.xmrto.api.QueryOrderParameters;
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
import org.json.JSONException;
import org.json.JSONObject;
@ -128,16 +128,9 @@ public class XmrToApiImpl implements XmrToApi, XmrToApiCall {
if (request != null) {
final RequestBody body = RequestBody.create(
MediaType.parse("application/json"), request.toString());
return new Request.Builder()
.url(url)
.post(body)
.build();
return OkHttpHelper.getPostRequest(url, body);
} else {
return new Request.Builder()
.url(url)
.get()
.build();
return OkHttpHelper.getGetRequest(url);
}
}
}

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="45"
android:endColor="@color/moneroStreetA"
android:startColor="@color/moneroStreetB"
android:type="linear" />
<corners android:radius="56dp" />
</shape>

@ -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/gradientPink"
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z" />
</vector>

@ -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/gradientPink"
android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z" />
</vector>

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

@ -1,33 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="230.0"
android:viewportWidth="230.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M0 0L0 230L230 230L230 0L0 0z" />
<path
android:fillColor="#FF000000"
android:pathData="M10 10L10 80L80 80L80 10L10 10M110 10L110 20L120 20L120 10L110 10M130 10L130 30L140 30L140 10L130 10M150 10L150 80L220 80L220 10L150 10z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M20 20L20 70L70 70L70 20L20 20M160 20L160 70L210 70L210 20L160 20z" />
<path
android:fillColor="#FF000000"
android:pathData="M30 30L30 60L60 60L60 30L30 30M90 30L90 40L100 40L100 30L90 30M110 30L110 40L120 40L120 30L110 30M170 30L170 60L200 60L200 30L170 30M130 40L130 50L120 50L120 60L110 60L110 50L100 50L100 70L90 70L90 90L50 90L50 120L60 120L60 130L50 130L50 140L60 140L60 130L70 130L70 140L90 140L90 180L100 180L100 170L110 170L110 180L120 180L120 190L90 190L90 220L100 220L100 210L110 210L110 220L130 220L130 210L140 210L140 220L160 220L160 210L170 210L170 220L190 220L190 210L180 210L180 200L200 200L200 220L220 220L220 210L210 210L210 200L220 200L220 190L210 190L210 180L200 180L200 190L180 190L180 170L160 170L160 160L180 160L180 140L160 140L160 130L180 130L180 120L190 120L190 110L200 110L200 120L220 120L220 100L200 100L200 90L190 90L190 110L180 110L180 100L160 100L160 90L130 90L130 100L120 100L120 70L130 70L130 80L140 80L140 70L130 70L130 60L140 60L140 40L130 40z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M100 70L100 100L90 100L90 110L80 110L80 100L70 100L70 110L60 110L60 120L70 120L70 130L90 130L90 140L100 140L100 150L110 150L110 140L120 140L120 160L110 160L110 170L120 170L120 180L130 180L130 200L140 200L140 180L150 180L150 190L170 190L170 180L150 180L150 160L160 160L160 150L150 150L150 130L160 130L160 110L150 110L150 100L130 100L130 120L140 120L140 110L150 110L150 130L130 130L130 140L120 140L120 120L110 120L110 130L100 130L100 120L90 120L90 110L100 110L100 100L110 100L110 110L120 110L120 100L110 100L110 70L100 70z" />
<path
android:fillColor="#FF000000"
android:pathData="M10 90L10 120L30 120L30 130L10 130L10 140L30 140L30 130L40 130L40 120L30 120L30 110L20 110L20 100L40 100L40 90L10 90M70 110L70 120L80 120L80 110L70 110M200 130L200 140L190 140L190 150L200 150L200 170L210 170L210 160L220 160L220 150L210 150L210 130L200 130M10 150L10 220L80 220L80 150L10 150M130 150L130 160L140 160L140 150L130 150z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M20 160L20 210L70 210L70 160L20 160z" />
<path
android:fillColor="#FF000000"
android:pathData="M30 170L30 200L60 200L60 170L30 170M130 170L130 180L140 180L140 170L130 170z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M200 190L200 200L210 200L210 190L200 190M110 200L110 210L120 210L120 200L110 200M150 200L150 210L160 210L160 200L150 200z" />
</vector>

@ -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="#FF000000"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
</vector>

@ -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/moneroBlack"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>

@ -0,0 +1,13 @@
<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:fillAlpha=".3"
android:fillColor="#FF000000"
android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z" />
<path
android:fillColor="#FF000000"
android:pathData="M6.67,14.86L12,21.49v0.01l0.01,-0.01 5.33,-6.63C17.06,14.65 15.03,13 12,13s-5.06,1.65 -5.33,1.86z" />
</vector>

@ -0,0 +1,13 @@
<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:fillAlpha=".3"
android:fillColor="#FF000000"
android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z" />
<path
android:fillColor="#FF000000"
android:pathData="M4.79,12.52l7.2,8.98H12l0.01,-0.01 7.2,-8.98C18.85,12.24 16.1,10 12,10s-6.85,2.24 -7.21,2.52z" />
</vector>

@ -0,0 +1,13 @@
<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:fillAlpha=".3"
android:fillColor="#FF000000"
android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z" />
<path
android:fillColor="#FF000000"
android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z" />
</vector>

@ -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="#FF000000"
android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z" />
</vector>

@ -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="#FF000000"
android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zM17.04,15.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z" />
</vector>

@ -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="#FF000000"
android:pathData="M20.5,9.5c0.28,0 0.55,0.04 0.81,0.08L24,6c-3.34,-2.51 -7.5,-4 -12,-4S3.34,3.49 0,6l12,16 3.5,-4.67L15.5,14.5c0,-2.76 2.24,-5 5,-5zM23,16v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S18,13.12 18,14.5L18,16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zM22,16h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5L22,16z" />
</vector>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp">
@ -16,35 +17,85 @@
android:layout_height="wrap_content"
android:orientation="vertical" />
<EditText
android:id="@+id/etDummy"
android:layout_width="0dp"
android:layout_height="0dp" />
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="start|center"
android:padding="8dp"
android:text="@string/label_daemon" />
<android.support.design.widget.TextInputLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:hint="@string/label_daemon">
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp">
<com.m2049r.xmrwallet.widget.DropDownEditText
android:id="@+id/etDaemonAddress"
style="@style/MoneroEdit.Small"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:hint="@string/prompt_daemon"
android:imeOptions="actionDone"
android:inputType="textWebEmailAddress|textNoSuggestions"
android:maxLines="1"
android:textIsSelectable="true" />
</android.support.design.widget.TextInputLayout>
android:layout_alignParentTop="true"
android:layout_marginStart="16dp"
android:layout_toStartOf="@+id/ibOption"
android:layout_toEndOf="@id/ibBookmark">
<ProgressBar
android:id="@+id/pbNode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:visibility="invisible" />
<LinearLayout
android:id="@+id/llNode"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="start|center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/tvNodeName"
style="@style/MoneroText.PosAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_refresh_black_24dp"
android:drawablePadding="8dp"
tools:text="monero-v9.monerujo.io" />
<TextView
android:id="@+id/tvNodeAddress"
style="@style/MoneroText.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
tools:text="128.130.233.151:18089" />
</LinearLayout>
</FrameLayout>
<ImageButton
android:id="@+id/ibOption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:padding="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:src="@drawable/ic_search_orange_24dp" />
</RelativeLayout>
<EditText
android:id="@+id/etDummy"
android:layout_width="0dp"
android:layout_height="0dp" />
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginStart="8dp"
android:gravity="start|center"
android:padding="8dp"
android:text="@string/label_login_wallets" />
@ -72,7 +123,7 @@
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="72dp"
app:layoutManager="LinearLayoutManager"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="@layout/item_wallet" />
</FrameLayout>

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/llNotice"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<TextView
android:id="@+id/tvPull"
android:layout_below="@+id/llNotice"
style="@style/MoneroLabel.Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="@string/node_pull_hint" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tvPull">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="72dp"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="@layout/item_node" />
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_margin="24dp"
android:background="@drawable/gradient_street_efab"
android:elevation="6dp">
<android.support.design.button.MaterialButton
android:id="@+id/fab"
style="@style/ExtendedFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@android:color/transparent"
android:backgroundTintMode="src_in"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/node_fab_add"
app:cornerRadius="56dp"
app:icon="@drawable/ic_add_white_24dp" />
</FrameLayout>
</RelativeLayout>

@ -25,7 +25,6 @@
android:visibility="gone" />
<LinearLayout
android:id="@+id/llAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">

@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/text_streetmode"
android:text="@string/menu_streetmode"
android:visibility="invisible" />
<LinearLayout

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@drawable/selector_login">
<ImageButton
android:id="@+id/ibBookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:src="@drawable/ic_bookmark_border_24dp" />
<LinearLayout
android:id="@+id/llNode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="8dp"
android:layout_toStartOf="@+id/ivPing"
android:layout_toEndOf="@id/ibBookmark"
android:gravity="start"
android:orientation="vertical">
<TextView
android:id="@+id/tvName"
style="@style/MoneroText.PosAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="monero-v9.monerujo.io" />
<TextView
android:id="@+id/tvAddress"
style="@style/MoneroText.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="128.130.233.151:18089" />
</LinearLayout>
<ImageView
android:id="@+id/ivPing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_signal_wifi_4_bar_black_24dp" />
</RelativeLayout>

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top"
android:orientation="horizontal"
android:weightSum="10">
<android.support.design.widget.TextInputLayout
android:id="@+id/etNodeHost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="8"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/node_address_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:maxLines="1"
android:textAlignment="textStart"
tools:text="node.supportxmr.com" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/etNodePort"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/node_port_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:maxLines="1"
android:textAlignment="textStart"
tools:text="18089" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/etNodeName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/data_top">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/node_name_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:maxLines="1"
android:textAlignment="textStart"
tools:text="supportxmr" />
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/data_top"
android:orientation="horizontal">
<android.support.design.widget.TextInputLayout
android:id="@+id/etNodeUser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/node_user_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:maxLines="1"
android:textAlignment="textStart"
tools:text="userxyz" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/etNodePass"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<android.support.design.widget.TextInputEditText
style="@style/MoneroEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/node_pass_hint"
android:imeOptions="actionDone"
android:inputType="text"
android:maxLines="1"
android:textAlignment="textStart"
tools:text="secret" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top_first">
<TextView
android:id="@+id/tvResultLabel"
style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="4dp"
android:text="@string/node_result_label"
android:textAlignment="textStart" />
<TextView
android:id="@+id/tvResult"
style="@style/MoneroText.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_alignBaseline="@+id/tvResultLabel"
android:layout_toEndOf="@+id/tvResultLabel"
android:textAlignment="textStart"
tools:text="Blockheight: 1710998 (v9), Ping: 187ms" />
</RelativeLayout>
</LinearLayout>
</FrameLayout>

@ -5,7 +5,6 @@
android:checkableBehavior="single"
android:orderInCategory="100" />
<group
android:id="@+id/accounts_new"
android:checkableBehavior="none"
android:orderInCategory="200">
<item

@ -2,13 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_stagenet"
android:checkable="true"
android:orderInCategory="100"
android:title="@string/menu_stagenet"
app:showAsAction="never" />
<item
android:id="@+id/action_language"
android:orderInCategory="200"

@ -0,0 +1,11 @@
<?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_help_node"
android:icon="@drawable/ic_help_white_24dp"
android:orderInCategory="500"
android:title="@string/menu_help"
app:showAsAction="ifRoom" />
</menu>

@ -29,10 +29,8 @@
<string name="label_send_done">Fertig</string>
<string name="label_receive_info_gen_qr_code">Berühren für QR-Code</string>
<string name="info_send_prio_fees">Höhere Priorität = Höhere Gebühr</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_ledger_enabled">Ledger aktiviert - Tippe für mehr Infos.</string>
<string name="info_xmrto"><![CDATA[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Kurs: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Fortgeschritten:</string>
<string name="label_send_btc_xmrto_info">Besuche XMR.TO für Support &amp; Nachverfolgung</string>
<string name="label_send_btc_xmrto_key_lb">Geheimer Schlüssel\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Geheimer Schlüssel</string>
@ -96,7 +92,6 @@
<string name="backup_progress">Backup wird erstellt</string>
<string name="archive_progress">Archivierung wird ausgeführt</string>
<string name="rename_progress">Umbenennung wird ausgeführt</string>
<string name="open_progress">Daemon Verbindung wird getestet</string>
<string name="changepw_progress">Ändere Passwort</string>
<string name="service_progress">Fertigstellen &#8230;\nDies kann einen Moment dauern!</string>
@ -108,13 +103,11 @@
<string name="changepw_success">Passwort geändert</string>
<string name="label_daemon">Node</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Lade Wallet &#8230;</string>
<string name="status_wallet_unloaded">Wallet gespeichert</string>
<string name="status_wallet_unload_failed">Walletspeicherung fehlgeschlagen!</string>
<string name="status_wallet_connecting">Verbinde &#8230;</string>
<string name="status_wallet_connect_failed">Verbindung zum Node fehlgeschlagen!\nPrüfe Username/Passwort</string>
<string name="status_wallet_connect_timeout">Node Zeitüberschreitung!\nNochmal oder anderen Node versuchen.</string>
<string name="status_wallet_node_invalid">Node ungültig!\nVersuche anderen.</string>
<string name="status_wallet_connect_ioex">Kann Node nicht erreichen!\nNochmal oder anderen Node versuchen.</string>
@ -194,7 +187,6 @@
<string name="generate_wallet_dot">Kann nicht mit einem . beginnen</string>
<string name="generate_wallet_creating">Erstelle Wallet</string>
<string name="generate_wallet_created">Wallet erstellt</string>
<string name="generate_wallet_create_failed">Walleterstellung fehlgeschlagen</string>
<string name="generate_restoreheight_error">Gib eine Blocknummer oder ein Datum (JJJJ-MM-TT) ein</string>
@ -274,7 +266,6 @@
<string name="tx_pending">AUSSTEHEND</string>
<string name="tx_failed">FEHLGESCHLAGEN</string>
<string name="receive_paymentid_hint">Zahlungs-ID (optional)</string>
<string name="receive_amount_hint">Betrag</string>
<string name="receive_cannot_open">Konnte Wallet nicht öffnen!</string>
<string name="receive_paymentid_invalid">16 oder 64 Hex-Zeichen (0&#8211;9,a&#8211;f)</string>
@ -284,8 +275,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR keine Nummer</string>
<string name="receive_title">Empfangen</string>
<string name="details_alert_message">Sensible Daten werden angezeigt.\nSchau über deine Schulter!</string>
<string name="details_alert_yes">Ich bin sicher</string>
<string name="details_alert_no">Nein, doch nicht!</string>
@ -295,20 +284,12 @@
<string name="archive_alert_yes">Ja, mach das!</string>
<string name="archive_alert_no">Nein, danke!</string>
<string-array name="priority">
<item>Priorität Standard</item>
<item>Priorität Gering</item>
<item>Priorität Mittel</item>
<item>Priorität Hoch</item>
</string-array>
<string name="fab_create_new">Neues Wallet erstellen</string>
<string name="fab_restore_viewonly">View Only Wallet wiederherstellen</string>
<string name="fab_restore_key">Mit privaten Schlüsseln wiederherstellen</string>
<string name="fab_restore_seed">Mit 25 Wörter Seed wiederherstellen</string>
<string name="accounts_drawer_new">Konto erstellen</string>
<string name="accounts_drawer_title">Konten</string>
<string name="accounts_new">Neues Konto #%1$d hinzugefügt</string>
<string name="tx_account">Konto #</string>
@ -330,7 +311,6 @@
<string name="open_wallet_ledger_missing">Bitte Ledger (wieder-)anschließen!</string>
<string name="accounts_progress_new">Erzeuge Konto</string>
<string name="accounts_progress_update">Aktualisiere Wallet</string>
<string name="toast_ledger_attached">%1$s angesteckt</string>
<string name="toast_ledger_detached">%1$s abgesteckt</string>
@ -357,5 +337,31 @@
<string name="menu_info">Details</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -28,10 +28,8 @@
<string name="label_send_done">Έγινε</string>
<string name="label_receive_info_gen_qr_code">Πατήστε για QR κωδικό</string>
<string name="info_send_prio_fees">Υψηλότερη προτεραιότητα = Υψηλότερα Κόμιστρα</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[
@ -78,8 +76,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Rate: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Για προχωρημένους:</string>
<string name="label_send_btc_xmrto_info">Επίσκεψη στο xmr.to για υποστήριξη &amp; με εντοπισμό συναλλαγής</string>
<string name="label_send_btc_xmrto_key_lb">Μυστικό Κλειδί\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Μυστικό Κλειδί</string>
@ -94,7 +90,6 @@
<string name="backup_progress">Δημιουργία αντίγραφου ασφαλείας σε εξέλιξη</string>
<string name="archive_progress">Αρχειοθέτηση σε εξέλιξη</string>
<string name="rename_progress">Μετονομασία σε εξέλιξη</string>
<string name="open_progress">Έλεγχος σύνδεσης δαίμονα</string>
<string name="service_progress">Μάζεμα των πραγμάτων &#8230;\nΑυτό μπορεί να διαρκέσει λίγο!</string>
@ -103,13 +98,11 @@
<string name="rename_failed">Η μετονομασία απέτυχε!</string>
<string name="label_daemon">Κόμβος(Δαίμονας)</string>
<string name="prompt_daemon">([&lt;χρήστης&gt;:&lt;κωδικός&gt;@]&lt;δαίμονας&gt;[:&lt;πόρτα&gt;])</string>
<string name="status_wallet_loading">Φόρτωση Πορτοφολιού &#8230;</string>
<string name="status_wallet_unloaded">Πορτοφόλι αποθηκεύτηκε</string>
<string name="status_wallet_unload_failed">Η αποθήκευση του πορτοφολιού απέτυχε!</string>
<string name="status_wallet_connecting">Σύνδεση &#8230;</string>
<string name="status_wallet_connect_failed">Η σύνδεση με τον κόμβο απέτυχε!\nΈλεγξε χρήστη/κωδικό</string>
<string name="status_wallet_connect_timeout">Η σύνδεση με τον κόμβο έχει λήξει!\nΠροσπάθησε πάλι ή με άλλον.</string>
<string name="status_wallet_node_invalid">Ο κόμβος είναι μη έγκυρος!\nΠροσπάθησε με άλλον.</string>
<string name="status_wallet_connect_ioex">Δεν είναι δυνατή η πρόσβαση στον κόμβο!\nΠροσπάθησε πάλι ή με άλλον.</string>
@ -169,7 +162,6 @@
<string name="generate_wallet_dot">Δεν γίνεται να ξεκινάει με .</string>
<string name="generate_wallet_creating">Το πορτοφόλι δημιουργείται</string>
<string name="generate_wallet_created">Το Πορτοφόλι δημιουργήθηκε</string>
<string name="generate_wallet_create_failed">Η δημιουργία πορτοφολιού απέτυχε</string>
<string name="generate_restoreheight_error">Βάλε αριθμό ή ημερομηνία (YYYY-MM-DD δλδ χρονιά-μήνας-μέρα)</string>
@ -248,7 +240,6 @@
<string name="tx_pending">ΕΚΚΡΕΜΗ</string>
<string name="tx_failed">ΑΠΕΤΥΧΕ</string>
<string name="receive_paymentid_hint">ID Πληρωμής(Payment ID) (προαιρετικό)</string>
<string name="receive_amount_hint">Ποσό</string>
<string name="receive_cannot_open">Δεν ήταν δυνατό το άνοιγμα του πορτοφολιού!</string>
<string name="receive_paymentid_invalid">16 ή 64 χαρακτήρες Hex (0&#8211;9,a&#8211;f)</string>
@ -258,8 +249,6 @@
<string name="receive_amount_negative">Ελάχιστο 0</string>
<string name="receive_amount_nan">XMR δεν υπάρχει αριθμός</string>
<string name="receive_title">Λήψη</string>
<string name="details_alert_message">Θα εμφανιστούν τώρα ευαίσθητα δεδομένα. \nΠρόσεχε ποιος είναι πίσω σου!</string>
<string name="details_alert_yes">Είμαι ασφαλής</string>
<string name="details_alert_no">Πήγαινε με πίσω!</string>
@ -269,13 +258,6 @@
<string name="archive_alert_yes">Ναι, κάνε αυτό!</string>
<string name="archive_alert_no">Όχι ευχαριστώ!</string>
<string-array name="priority">
<item>Προεπιλεγμένη Προτεραιότητα</item>
<item>Χαμηλή Προτεραιότητα</item>
<item>Μέτρια Προτεραιότητα</item>
<item>Υψηλή Προτεραιότητα</item>
</string-array>
<string name="fab_create_new">Δημιουργία νέου πορτοφολιού</string>
<string name="fab_restore_viewonly">Επαναφορά πορτοφολιού προβολής-μόνο</string>
<string name="fab_restore_key">Επαναφορά πορτοφολιού από ιδιωτικά κλειδιά</string>
@ -308,7 +290,6 @@
<string name="generate_empty_passwordB">Passphrase may not be empty</string>
<string name="generate_crazypass_label">Wallet Files Restore Password</string>
<string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string>
@ -329,7 +310,6 @@
<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>
@ -356,5 +336,31 @@
<string name="menu_info">Λεπτομέριες</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,7 +29,6 @@
<string name="label_send_done">Hecho</string>
<string name="label_receive_info_gen_qr_code">Toca para mostrar código QR</string>
<string name="info_send_prio_fees">Mayor Prioridad = Mayor Comisión</string>
<string name="info_paymentid_intergrated">ID de pago integrado</string>
<string name="info_prepare_tx">Preparando tu transacción</string>
@ -44,7 +43,6 @@
<string name="backup_progress">Copia de seguridad en progreso</string>
<string name="archive_progress">Archivado en progreso</string>
<string name="rename_progress">Cambio de nombre en progreso</string>
<string name="open_progress">Comprobando conexión con el daemon</string>
<string name="changepw_progress">Cambiando contraseña en progreso</string>
<string name="service_progress">Guardando todo\n¡Puede llevar un tiempo!</string>
@ -56,13 +54,11 @@
<string name="changepw_success">Contraseña cambiada</string>
<string name="label_daemon">Nodo</string>
<string name="prompt_daemon">([&lt;usuario&gt;:&lt;contraseña&gt;@]&lt;daemon&gt;[:&lt;puerto&gt;])</string>
<string name="status_wallet_loading">Cargando monedero&#8230;</string>
<string name="status_wallet_unloaded">Monedero guardada</string>
<string name="status_wallet_unload_failed">¡Guardado de monedero fallido!</string>
<string name="status_wallet_connecting">Conectando&#8230;</string>
<string name="status_wallet_connect_failed">¡Conexión con el nodo fallida!\nComprueba el usuario/contraseña</string>
<string name="status_wallet_connect_timeout">¡Conexión con el nodo ha expirado!\nInténtalo de nuevo o prueba otro.</string>
<string name="status_wallet_node_invalid">¡Nodo inválido!\nInténtalo con otro.</string>
<string name="status_wallet_connect_ioex">¡No se puede alcanzar el nodo!\nInténtalo de nuevo o prueba otro.</string>
@ -138,7 +134,6 @@
<string name="generate_wallet_dot">No puede empezar con .</string>
<string name="generate_wallet_creating">Creando monedero</string>
<string name="generate_wallet_created">Monedero creada</string>
<string name="generate_wallet_create_failed">Creación de monedero fallida</string>
<string name="generate_restoreheight_error">Introduce un número o una fecha (AAAA-MM-DD)</string>
@ -206,7 +201,6 @@
<string name="tx_pending">PENDIENTE</string>
<string name="tx_failed">FALLIDO</string>
<string name="receive_paymentid_hint">ID de Pago (opcional)</string>
<string name="receive_amount_hint">Monto</string>
<string name="receive_cannot_open">¡No se ha podido abrir el monedero!</string>
<string name="receive_paymentid_invalid">16 o 64 caracteres hexadecimales (0&#8211;9,a&#8211;f)</string>
@ -216,8 +210,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR no es un número</string>
<string name="receive_title">Recibir</string>
<string name="details_alert_message">Se va a mostrar información delicada.\n¡Mira por encima del hombro!</string>
<string name="details_alert_yes">Estoy seguro</string>
<string name="details_alert_no">¡Llévame de vuelta!</string>
@ -227,13 +219,6 @@
<string name="archive_alert_yes">¡Sí, haz eso!</string>
<string name="archive_alert_no">¡No gracias!</string>
<string-array name="priority">
<item>Prioridad por Defecto</item>
<item>Prioridad Baja</item>
<item>Prioridad Media</item>
<item>Prioridad Alta</item>
</string-array>
<string name="fab_create_new">Crear nuevo monedero</string>
<string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string>
<string name="fab_restore_key">Restaurar monedero con claves privadas</string>
@ -267,7 +252,6 @@
<string name="text_noretry">Oh-oh, parece que XMR.TO no está disponible ahora!</string>
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Cambio: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avanzado</string>
<string name="label_send_btc_xmrto_info">Visita https://xmr.to para soporte y rastreo</string>
<string name="label_send_btc_xmrto_key_lb">Clave secreta\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Clave secreta XMR.TO</string>
@ -290,11 +274,9 @@
<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_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_title">Cuentas</string>
<string name="accounts_new">Nueva cuenta agregada #%1$d</string>
<string name="tx_account"># de cuenta</string>
@ -316,7 +298,6 @@
<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>
@ -343,5 +324,31 @@
<string name="menu_info">Detalles</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Tehtud</string>
<string name="label_receive_info_gen_qr_code">Puuduta QR koodi saamiseks</string>
<string name="info_send_prio_fees">Suurem tähtsus = kõrgemad teenustasud</string>
<string name="info_xmrto_enabled">Bitcoini maksed sisse lülitatud, puuduta lisainfo saamiseks.</string>
<string name="info_crazypass_enabled">CrAzYpass sisse lülitatud, puuduta lisainfo saamiseks.</string>
<string name="info_ledger_enabled">Ledger\'i tugi sisse lülitatud, puuduta lisainfo saamiseks.</string>
<string name="info_xmrto"><![CDATA[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Kurss: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Edasijõudnutele:</string>
<string name="label_send_btc_xmrto_info">Külasta xmr.to lisainfo saamiseks &amp; jälgin</string>
<string name="label_send_btc_xmrto_key_lb">Privaatvõti\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO privaatvõti</string>
@ -95,7 +91,6 @@
<string name="backup_progress">Teostan tagavarakoopiat</string>
<string name="archive_progress">Arhiveerin</string>
<string name="rename_progress">Nimetan ümber</string>
<string name="open_progress">Kontrollin ühendust serveriga</string>
<string name="changepw_progress">Vahetan parooli</string>
<string name="service_progress">Teen ettevalmistusi &#8230;\nSee võib aega võtta!</string>
@ -107,13 +102,11 @@
<string name="changepw_success">Parool vahetatud</string>
<string name="label_daemon">Server</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Laen rahakotti &#8230;</string>
<string name="status_wallet_unloaded">Rahakott salvestatud</string>
<string name="status_wallet_unload_failed">Rahakoti salvestamine ebaõnnestus!</string>
<string name="status_wallet_connecting">Ühendun &#8230;</string>
<string name="status_wallet_connect_failed">Serveriga ühendumine ebaõnnestus!\nKontrolli kasutajanime/parooli</string>
<string name="status_wallet_connect_timeout">Serveriga ühendumine võttis liiga kaua aega!\nProovi uuesti või vali teine server.</string>
<string name="status_wallet_node_invalid">Ebasobilik server!\nVali mõni teine.</string>
<string name="status_wallet_connect_ioex">Server ei vastanud!\nProovi uuesti või vali teine server.</string>
@ -194,7 +187,6 @@
<string name="generate_wallet_dot">Nimi ei saa alata märgiga .</string>
<string name="generate_wallet_creating">Loon rahakotti</string>
<string name="generate_wallet_created">Rahakott loodud</string>
<string name="generate_wallet_create_failed">Rahakoti loomine ebaõnnestus</string>
<string name="generate_restoreheight_error">Sisesta plokinumber või kuupäev (YYYY-MM-DD)</string>
@ -279,7 +271,6 @@
<string name="tx_pending">OOTEL</string>
<string name="tx_failed">EBAÕNNESTUS</string>
<string name="receive_paymentid_hint">Makse ID (valikuline)</string>
<string name="receive_amount_hint">Kogus</string>
<string name="receive_desc_hint">Kommentaarid (valikuline)</string>
<string name="receive_cannot_open">Rahakotti ei õnnestunud avada!</string>
@ -290,8 +281,6 @@
<string name="receive_amount_negative">Vähemalt 0</string>
<string name="receive_amount_nan">XMR pole arv</string>
<string name="receive_title">Küsi raha</string>
<string name="details_alert_message">Nüüd näidatakse tundlikku infot.\nPiilu oma seljataha!</string>
<string name="details_alert_yes">Olen turvalises kohas</string>
<string name="details_alert_no">Vii mind tagasi!</string>
@ -301,20 +290,12 @@
<string name="archive_alert_yes">Täpselt nii!</string>
<string name="archive_alert_no">Ei, tänan!</string>
<string-array name="priority">
<item>Tavaline tähtsus</item>
<item>Väike tähtsus</item>
<item>Keskmine tähtsus</item>
<item>Suur tähtsus</item>
</string-array>
<string name="fab_create_new">Loo uus rahakott</string>
<string name="fab_restore_viewonly">Taasta rahakott vaatamiseks</string>
<string name="fab_restore_key">Taasta rahakott privaatvõtmetest</string>
<string name="fab_restore_seed">Taasta rahakott 25-sõnalisest seemnest</string>
<string name="accounts_drawer_new">Loo konto</string>
<string name="accounts_drawer_title">Kontod</string>
<string name="accounts_new">Uus konto lisatud #%1$d</string>
<string name="tx_account">Konto #</string>
@ -336,7 +317,6 @@
<string name="open_wallet_ledger_missing">Palun (taas)ühenda seade Ledger</string>
<string name="accounts_progress_new">Loon kontot</string>
<string name="accounts_progress_update">Uuendan rahakotti</string>
<string name="toast_ledger_attached">%1$s lisatud</string>
<string name="toast_ledger_detached">%1$s eemaldatud</string>
@ -355,5 +335,31 @@
<string name="menu_info">Näita salajast infot</string>
<string name="menu_streetmode">Avalik režiim</string>
<string name="info_streetmode_enabled">Avalik režiim sisse lülitatud, puuduta lisainfo saamiseks.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Fait</string>
<string name="label_receive_info_gen_qr_code">Toucher pour le QR Code</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_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[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Taux : %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avancé :</string>
<string name="label_send_btc_xmrto_info">Visitez xmr.to pour l\'assistance &amp; le suivi</string>
<string name="label_send_btc_xmrto_key_lb">Clef Secrète\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Clef Secrète XMR.TO</string>
@ -96,7 +92,6 @@
<string name="backup_progress">Sauvegarde en cours</string>
<string name="archive_progress">Archivage en cours</string>
<string name="rename_progress">Renommage en cours</string>
<string name="open_progress">Vérification de la connexion au démon</string>
<string name="changepw_progress">Modification du mot de passe en cours</string>
<string name="service_progress">Remise en ordre &#8230;\nCela peut prendre un moment !</string>
@ -108,13 +103,11 @@
<string name="changepw_success">Modification du mot de passe réussie</string>
<string name="label_daemon">Nœud</string>
<string name="prompt_daemon">([&lt;utilisateur&gt;:&lt;mdp&gt;@]&lt;démon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Chargement du Portefeuille &#8230;</string>
<string name="status_wallet_unloaded">Portefeuille Sauvegardé</string>
<string name="status_wallet_unload_failed">Sauvegarde du Portefeuille Échouée !</string>
<string name="status_wallet_connecting">Connexion &#8230;</string>
<string name="status_wallet_connect_failed">Connexion au nœud échouée !\nVérifiez utilisateur/mot de passe</string>
<string name="status_wallet_connect_timeout">Connexion au nœud sans réponse !\nEssayez à nouveau ou un autre.</string>
<string name="status_wallet_node_invalid">Nœud invalide !\nEssayez un autre.</string>
<string name="status_wallet_connect_ioex">Nœud injoignable !\nEssayez à nouveau ou un autre.</string>
@ -196,7 +189,6 @@
<string name="generate_wallet_dot">Ne peut pas commencer par .</string>
<string name="generate_wallet_creating">Création du Portefeuille</string>
<string name="generate_wallet_created">Portefeuille créé</string>
<string name="generate_wallet_create_failed">Création du Portefeuille échouée</string>
<string name="generate_restoreheight_error">Entrer un nombre ou une date (YYYY-MM-DD)</string>
@ -276,7 +268,6 @@
<string name="tx_pending">EN ATTENTE</string>
<string name="tx_failed">ÉCHOUÉ</string>
<string name="receive_paymentid_hint">ID de Paiement (optionnel)</string>
<string name="receive_amount_hint">Montant</string>
<string name="receive_cannot_open">Ouverture du portefeuille impossible !</string>
<string name="receive_paymentid_invalid">16 ou 64 caractères héxadécimaux (0&#8211;9,a&#8211;f)</string>
@ -286,8 +277,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR pas un nombre</string>
<string name="receive_title">Recevoir</string>
<string name="details_alert_message">Des données sensible vont être affichées.\nRegardez autour de vous !</string>
<string name="details_alert_yes">C\'est bon</string>
<string name="details_alert_no">Non merci !</string>
@ -297,20 +286,12 @@
<string name="archive_alert_yes">Oui, procéder !</string>
<string name="archive_alert_no">Non merci !</string>
<string-array name="priority">
<item>Priority Défaut</item>
<item>Priority Faible</item>
<item>Priority Moyenne</item>
<item>Priority Élevée</item>
</string-array>
<string name="fab_create_new">Créer un nouveau portefeuille</string>
<string name="fab_restore_viewonly">Restaurer un portefeuille d\'audit</string>
<string name="fab_restore_key">Restaurer depuis la clef privée</string>
<string name="fab_restore_seed">Restaurer depuis la phrase mnémonique</string>
<string name="accounts_drawer_new">Créer un Compte</string>
<string name="accounts_drawer_title">Comptes</string>
<string name="accounts_new">Nouveau Compte #%1$d ajouté</string>
<string name="tx_account">Compte #</string>
@ -329,7 +310,6 @@
<string name="open_wallet_ledger_missing">Merci de (re)connecter le Ledger</string>
<string name="accounts_progress_new">Création du compte</string>
<string name="accounts_progress_update">Mise à jour du portefeuille</string>
<string name="toast_ledger_attached">%1$s connecté</string>
<string name="toast_ledger_detached">%1$s déconnecté</string>
@ -359,5 +339,31 @@
<string name="menu_info">Montrer les secrets !</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Mode Urbain</string>
<string name="info_streetmode_enabled">Mode Urbain activé, tapez pour plus d\'infos.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Kész</string>
<string name="label_receive_info_gen_qr_code">Koppints a QR-kódért</string>
<string name="info_send_prio_fees">Magasabb prioritás = magasabb tranzakciós díj</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_ledger_enabled">Ledger engedélyezve, koppints ide a részletekért</string>
<string name="info_xmrto"><![CDATA[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Arány: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Haladó:</string>
<string name="label_send_btc_xmrto_info">Segítségért és nyomonkövetésért látogass el az XMR.TO weboldalra</string>
<string name="label_send_btc_xmrto_key_lb">Titkos kulcs\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO titkos kulcs</string>
@ -95,7 +91,6 @@
<string name="backup_progress">Biztonsági mentés folyamatban</string>
<string name="archive_progress">Archiválás folyamatban</string>
<string name="rename_progress">Átnevezés folyamatban</string>
<string name="open_progress">Daemon-kapcsolat ellenőrzése</string>
<string name="changepw_progress">Jelszómódosítás folyamatban</string>
<string name="service_progress">Műveletek befejezése&#8230;\nEz eltarthat egy ideig!</string>
@ -107,13 +102,11 @@
<string name="changepw_success">Jelszó megváltoztatva</string>
<string name="label_daemon">Csomópont</string>
<string name="prompt_daemon">([&lt;felhasználó&gt;:&lt;jelszó&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Tárca betöltése&#8230;</string>
<string name="status_wallet_unloaded">Tárca mentve</string>
<string name="status_wallet_unload_failed">Sikertelen mentés!</string>
<string name="status_wallet_connecting">Kapcsolódás&#8230;</string>
<string name="status_wallet_connect_failed">Sikertelen kapcsolódás.\nEllenőrizd a felhasználónevet és jelszót.</string>
<string name="status_wallet_connect_timeout">A kapcsolódás időtúllépés miatt megszakadt.\nPróbáld újra vagy próbálkozz egy másikkal.</string>
<string name="status_wallet_node_invalid">Érvénytelen csomópont!\nPróbálkozz egy másikkal.</string>
<string name="status_wallet_connect_ioex">Nem lehet elérni a csomópontot!\nPróbáld újra vagy próbálkozz egy másikkal.</string>
@ -194,7 +187,6 @@
<string name="generate_wallet_dot">Nem kezdődhet ponttal.</string>
<string name="generate_wallet_creating">Tárca létrehozása</string>
<string name="generate_wallet_created">Tárca létrehozva</string>
<string name="generate_wallet_create_failed">Sikertelen tárcalétrehozás</string>
<string name="generate_restoreheight_error">Számot vagy dátumot (ÉÉÉÉ-HH-NN) adj meg</string>
@ -274,7 +266,6 @@
<string name="tx_pending">FÜGGŐBEN</string>
<string name="tx_failed">SIKERTELEN</string>
<string name="receive_paymentid_hint">Fizetési azonosító (opcionális)</string>
<string name="receive_amount_hint">Mennyiség</string>
<string name="receive_cannot_open">Nem sikerült megnyitni a tárcát!</string>
<string name="receive_paymentid_invalid">16 vagy 64 hexadecimális karakter (0&#8211;9,a&#8211;f)</string>
@ -284,8 +275,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR nem egy szám</string>
<string name="receive_title">Fogadás</string>
<string name="details_alert_message">Bizalmas adatok kerülnek megjelenítésre.\nBizonyosodj meg róla, hogy más nem látja!</string>
<string name="details_alert_yes">Mehet</string>
<string name="details_alert_no">Inkább ne!</string>
@ -295,20 +284,12 @@
<string name="archive_alert_yes">Igen, mehet!</string>
<string name="archive_alert_no">Inkább ne!</string>
<string-array name="priority">
<item>Alapértelmezett prioritás</item>
<item>Alacsony prioritás</item>
<item>Közepes prioritás</item>
<item>Magas prioritás</item>
</string-array>
<string name="fab_create_new">Új tárca létrehozása</string>
<string name="fab_restore_viewonly">Figyelőtárca visszaállítása</string>
<string name="fab_restore_key">Visszaállítás privát kulcsokkal</string>
<string name="fab_restore_seed">Visszaállítás mnemonikus maggal</string>
<string name="accounts_drawer_new">Számla létrehozása</string>
<string name="accounts_drawer_title">Számlák</string>
<string name="accounts_new">Új számla hozzáadva (#%1$d)</string>
<string name="tx_account">Számla #</string>
@ -330,7 +311,6 @@
<string name="open_wallet_ledger_missing">Csatlakoztasd (újra) a Ledgert</string>
<string name="accounts_progress_new">Számla létrehozása</string>
<string name="accounts_progress_update">Tárca frissítése</string>
<string name="toast_ledger_attached">%1$s csatlakoztatva</string>
<string name="toast_ledger_detached">%1$s leválasztva</string>
@ -357,5 +337,31 @@
<string name="menu_info">Részletek</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Fatto</string>
<string name="label_receive_info_gen_qr_code">Tocca per il codice QR</string>
<string name="info_send_prio_fees">Priorità più alta = Commissioni più alte</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_ledger_enabled">Ledger abilitato, tocca per maggiori informazioni.</string>
<string name="info_xmrto"><![CDATA[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Tasso: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avanzate:</string>
<string name="label_send_btc_xmrto_info">Visita xmr.to per supporto e tracciamento</string>
<string name="label_send_btc_xmrto_key_lb">Chiave segreta\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Chiave segreta XMR.TO</string>
@ -96,7 +92,6 @@
<string name="backup_progress">Backup in corso</string>
<string name="archive_progress">Archiviazione in corso</string>
<string name="rename_progress">Rinomina in corso</string>
<string name="open_progress">Controllando connessione con daemon</string>
<string name="changepw_progress">Modifica password in corso</string>
<string name="service_progress">Rimettendo le cose a posto &#8230;\nPuò richiedere del tempo!</string>
@ -108,13 +103,11 @@
<string name="changepw_success">Password cambiata</string>
<string name="label_daemon">Nodo</string>
<string name="prompt_daemon">([&lt;utente&gt;:&lt;password&gt;@]&lt;daemon&gt;[:&lt;porta&gt;])</string>
<string name="status_wallet_loading">Caricando portafoglio &#8230;</string>
<string name="status_wallet_unloaded">Portafoglio salvato</string>
<string name="status_wallet_unload_failed">Salvataggio portafoglio fallito!</string>
<string name="status_wallet_connecting">In connessione &#8230;</string>
<string name="status_wallet_connect_failed">Connessione con il nodo fallita!\nControlla username/password</string>
<string name="status_wallet_connect_timeout">Connessione con il nodo scaduta!\nProva di nuovo o provane un altro.</string>
<string name="status_wallet_node_invalid">Nodo invalido!\nProvane un altro.</string>
<string name="status_wallet_connect_ioex">Impossibile raggiungere il nodo!\nProva di nuovo o provane un altro.</string>
@ -195,7 +188,6 @@
<string name="generate_wallet_dot">Non può iniziare con .</string>
<string name="generate_wallet_creating">Portafoglio in creazione</string>
<string name="generate_wallet_created">Portafoglio creato</string>
<string name="generate_wallet_create_failed">Creazione del portafoglio fallita</string>
<string name="generate_restoreheight_error">Inserisci un numero o una data (AAAA-MM-GG)</string>
@ -275,7 +267,6 @@
<string name="tx_pending">IN ATTESA</string>
<string name="tx_failed">FALLITA</string>
<string name="receive_paymentid_hint">ID pagamento (opzionale)</string>
<string name="receive_amount_hint">Ammontare</string>
<string name="receive_cannot_open">Impossibile aprire il portafoglio!</string>
<string name="receive_paymentid_invalid">16 o 64 caratteri esadecimali (0&#8211;9,a&#8211;f)</string>
@ -285,8 +276,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR non è un numero</string>
<string name="receive_title">Ricevi</string>
<string name="details_alert_message">Verranno ora mostrati dati sensibili.\nGuardati alle spalle!</string>
<string name="details_alert_yes">Sono al sicuro</string>
<string name="details_alert_no">Torna indietro!</string>
@ -296,20 +285,12 @@
<string name="archive_alert_yes">Sì, procedi!</string>
<string name="archive_alert_no">No grazie!</string>
<string-array name="priority">
<item>Priority Default</item>
<item>Priority Bassa</item>
<item>Priority Media</item>
<item>Priority Alta</item>
</string-array>
<string name="fab_create_new">Crea un nuovo portafoglio</string>
<string name="fab_restore_viewonly">Recupera un portafoglio solo-visualizzazione</string>
<string name="fab_restore_key">Recupera un portafoglio dalle chiavi private</string>
<string name="fab_restore_seed">Recupera un portafoglio da un seed di 25 parole</string>
<string name="accounts_drawer_new">Crea Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Aggiunto nuovo account #%1$d</string>
<string name="tx_account">Account #</string>
@ -331,7 +312,6 @@
<string name="open_wallet_ledger_missing">(Ri)connetti il dispositivo Ledger</string>
<string name="accounts_progress_new">Creando account</string>
<string name="accounts_progress_update">Aggiornando portafoglio</string>
<string name="toast_ledger_attached">%1$s allegati</string>
<string name="toast_ledger_detached">%1$s allegati</string>
@ -358,5 +338,31 @@
<string name="menu_info">Mostra i segreti!</string>
<string name="menu_streetmode">Modalità strada</string>
<string name="info_streetmode_enabled">Modalità strada abilitata, tocca per maggiori informazioni.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Ferdig</string>
<string name="label_receive_info_gen_qr_code">Trykk for QR kode</string>
<string name="info_send_prio_fees">Høyere prioritet = høyere avgifter</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_ledger_enabled">Ledger tilgjengelig, trykk for mer info.</string>
<string name="info_xmrto"><![CDATA[
@ -78,8 +76,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Rate: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avansert:</string>
<string name="label_send_btc_xmrto_info">Besøk xmr.to for støtte og sporing</string>
<string name="label_send_btc_xmrto_key_lb">Hemmelig nøkkel\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Hemmelig nøkkel</string>
@ -94,7 +90,6 @@
<string name="backup_progress">Backup pågår</string>
<string name="archive_progress">Arkivering pågår</string>
<string name="rename_progress">Nytt navn gis</string>
<string name="open_progress">Sjekker daemon-tilkobling</string>
<string name="changepw_progress">[Passordforandring i gang]</string>
<string name="service_progress">Gjør ting ferdig &#8230;\nDette kan ta si tid!</string>
@ -106,13 +101,11 @@
<string name="changepw_success">[Passord forandra]</string>
<string name="label_daemon">Node</string>
<string name="prompt_daemon">([&lt;bruker&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Laster lommebok &#8230;</string>
<string name="status_wallet_unloaded">Lommebok lagra</string>
<string name="status_wallet_unload_failed">Lommeboklagring feila!</string>
<string name="status_wallet_connecting">Kobler til &#8230;</string>
<string name="status_wallet_connect_failed">Node-tiklobling feila!\nSjekk brukernavn/passord</string>
<string name="status_wallet_connect_timeout">Node-tilkobling tok for lang tid!\nPrøv på nytt eller med en annen.</string>
<string name="status_wallet_node_invalid">Noden er ugyldig!\nPrøv en annen.</string>
<string name="status_wallet_connect_ioex">Kan ikke nå node!\nPrøv på nytt eller med en annen.</string>
@ -193,7 +186,6 @@
<string name="generate_wallet_dot">Kan ikke begynne med .</string>
<string name="generate_wallet_creating">Lager lommebok</string>
<string name="generate_wallet_created">Lommebok lagd</string>
<string name="generate_wallet_create_failed">Klarte ikke å lage lommebok</string>
<string name="generate_restoreheight_error">Skriv inn nummer eller dato (ÅÅÅÅ-MM-DD)</string>
@ -273,7 +265,6 @@
<string name="tx_pending">VENTENDE</string>
<string name="tx_failed">FEILA</string>
<string name="receive_paymentid_hint">Betalings-ID (valgfritt)</string>
<string name="receive_amount_hint">Mengde</string>
<string name="receive_cannot_open">Kunne ikke åpne lommebok!</string>
<string name="receive_paymentid_invalid">16 eller 64 heks-karakterer (0&#8211;9,a&#8211;f)</string>
@ -283,8 +274,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR ikke et tall</string>
<string name="receive_title">Motta</string>
<string name="details_alert_message">Sensitive data vil nå bli vist.\nSe over skulderen din!</string>
<string name="details_alert_yes">Jeg er trygg</string>
<string name="details_alert_no">Ta meg tilbake!</string>
@ -294,20 +283,12 @@
<string name="archive_alert_yes">Ja, gjør det!</string>
<string name="archive_alert_no">Nei takk!</string>
<string-array name="priority">
<item>Prioritet Standard</item>
<item>Prioritet Lav</item>
<item>Prioritet Middels</item>
<item>Prioritet Høy</item>
</string-array>
<string name="fab_create_new">Lag ny lommebok</string>
<string name="fab_restore_viewonly">Gjenoprett bare-se lommebok</string>
<string name="fab_restore_key">Gjenoprett lommebok fra private nøkler</string>
<string name="fab_restore_seed">Gjenoprett lommebok fra 25-ord seed</string>
<string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string>
@ -329,7 +310,6 @@
<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>
@ -356,5 +336,31 @@
<string name="menu_info">Detaljer</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Klaar</string>
<string name="label_receive_info_gen_qr_code">Tik voor QR-code</string>
<string name="info_send_prio_fees">Hogere prioriteit = hogere kosten</string>
<string name="info_xmrto_enabled">BTC-betaling ingeschakeld. Tik voor meer info.</string>
<string name="info_crazypass_enabled">CrAzYpass ingeschakeld. Tik voor meer info.</string>
<string name="info_ledger_enabled">Ledger ingeschakeld. Tik voor meer info.</string>
<string name="info_xmrto"><![CDATA[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Koers: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Geavanceerd:</string>
<string name="label_send_btc_xmrto_info">Ga naar xmr.to voor hulp en traceren</string>
<string name="label_send_btc_xmrto_key_lb">Geheime sleutel\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Geheime sleutel XMR.TO</string>
@ -95,7 +91,6 @@
<string name="backup_progress">Back-up wordt gemaakt</string>
<string name="archive_progress">Wordt gearchiveerd</string>
<string name="rename_progress">Naam wordt gewijzigd</string>
<string name="open_progress">Verbinding met node controleren</string>
<string name="changepw_progress">Wachtwoord wordt gewijzigd</string>
<string name="service_progress">Bezig met voltooien…\nDit kan even duren.</string>
@ -107,13 +102,11 @@
<string name="changepw_success">Wachtwoord is gewijzigd</string>
<string name="label_daemon">Node</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Portemonnee laden…</string>
<string name="status_wallet_unloaded">Portemonnee opgeslagen</string>
<string name="status_wallet_unload_failed">Portemonnee opslaan mislukt!</string>
<string name="status_wallet_connecting">Verbinden…</string>
<string name="status_wallet_connect_failed">Geen verbinding met node.\nControleer naam/wachtwoord.</string>
<string name="status_wallet_connect_timeout">Time-out verbinding met node.\nProbeer opnieuw of andere.</string>
<string name="status_wallet_node_invalid">Node ongeldig.\nProbeer een andere.</string>
<string name="status_wallet_connect_ioex">Kan node niet bereiken.\nProbeer opnieuw of andere.</string>
@ -191,7 +184,6 @@
<string name="generate_wallet_dot">Mag niet beginnen met .</string>
<string name="generate_wallet_creating">Portemonnee wordt gemaakt</string>
<string name="generate_wallet_created">Portemonnee is gemaakt</string>
<string name="generate_wallet_create_failed">Portemonnee maken mislukt!</string>
<string name="generate_restoreheight_error">Voer getal of datum (JJJJ-MM-DD) in</string>
@ -272,7 +264,6 @@
<string name="tx_pending">WACHTEN</string>
<string name="tx_failed">MISLUKT</string>
<string name="receive_paymentid_hint">Betalings-ID (optioneel)</string>
<string name="receive_amount_hint">Bedrag</string>
<string name="receive_cannot_open">Kan portemonnee niet openen!</string>
<string name="receive_paymentid_invalid">16 of 64 hexadecimale tekens (09, af)</string>
@ -282,8 +273,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR geen getal</string>
<string name="receive_title">Ontvangen</string>
<string name="details_alert_message">Nu worden vertrouwelijke gegevens getoond.\nKijk achter je!</string>
<string name="details_alert_yes">Het is veilig.</string>
<string name="details_alert_no">Ga terug!</string>
@ -293,20 +282,12 @@
<string name="archive_alert_yes">Ja, doe dat!</string>
<string name="archive_alert_no">Nee, niet doen!</string>
<string-array name="priority">
<item>Prioriteit Normaal</item>
<item>Prioriteit Laag</item>
<item>Prioriteit Normaal</item>
<item>Prioriteit Hoog</item>
</string-array>
<string name="fab_create_new">Nieuwe portemonnee</string>
<string name="fab_restore_viewonly">Alleen-lezen portemonnee herstellen</string>
<string name="fab_restore_key">Portemonnee herstellen met privésleutels</string>
<string name="fab_restore_seed">Voer je 25 woorden in</string>
<string name="accounts_drawer_new">Account maken</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Nieuw account #%1$d toegevoegd</string>
<string name="tx_account">Accountnr.</string>
@ -328,7 +309,6 @@
<string name="open_wallet_ledger_missing">Maak (opnieuw) verbinding met Ledger</string>
<string name="accounts_progress_new">Account maken…</string>
<string name="accounts_progress_update">Portemonnee bijwerken…</string>
<string name="toast_ledger_attached">%1$s gekoppeld</string>
<string name="toast_ledger_detached">%1$s losgemaakt</string>
@ -354,5 +334,31 @@
<string name="menu_info">Details</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Concluído</string>
<string name="label_receive_info_gen_qr_code">Toque para ver o código QR</string>
<string name="info_send_prio_fees">Maior Prioridade = Maiores Taxas</string>
<string name="info_xmrto_enabled">Pagamento em BTC ativado, toque para mais informações.</string>
<string name="info_crazypass_enabled">SeNHaLoUCa ativada, toque para mais informações.</string>
<string name="info_ledger_enabled">Ledger ativada, toque para mais informações.</string>
<string name="info_xmrto"><![CDATA[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Cotação: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avançado:</string>
<string name="label_send_btc_xmrto_info">Visite o XMR.TO para suporte &amp; rastreio da ordem</string>
<string name="label_send_btc_xmrto_key_lb">Chave secreta\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Chave secreta XMR.TO</string>
@ -95,7 +91,6 @@
<string name="backup_progress">Criando backup</string>
<string name="archive_progress">Arquivando</string>
<string name="rename_progress">Renomeando</string>
<string name="open_progress">Verificando conexão</string>
<string name="changepw_progress">Alterando senha</string>
<string name="service_progress">Finalizando o processo &#8230;\nPode demorar um pouco</string>
@ -107,14 +102,12 @@
<string name="changepw_success">Senha alterada</string>
<string name="label_daemon"></string>
<string name="prompt_daemon">([&lt;usuário&gt;:&lt;senha&gt;@]&lt;daemon&gt;[:&lt;porta&gt;])</string>
<string name="status_wallet_loading">Carregando &#8230;</string>
<string name="status_wallet_unloaded">Carteira salva</string>
<string name="status_wallet_unload_failed">Erro ao salvar carteira</string>
<string name="status_wallet_connecting">Conectando &#8230;</string>
<string name="status_wallet_connect_failed">Erro ao conectar!\Verifique o usuário e senha</string>
<string name="status_wallet_connect_wrongversion">Versão do nó incompatível - por favor faça a atualização!</string>
<string name="status_wallet_connect_timeout">Tempo limite de conexão ao nó!\nTente novamente ou escolha outro.</string>
<string name="status_wallet_node_invalid">Nó inválido!\nEscolha outro.</string>
<string name="status_wallet_connect_ioex">Não foi possível conectar ao nó!\nTente novamente ou escolha outro.</string>
@ -193,7 +186,6 @@
<string name="generate_wallet_dot">Não pode começar com .</string>
<string name="generate_wallet_creating">Criando carteira</string>
<string name="generate_wallet_created">Carteira criada</string>
<string name="generate_wallet_create_failed">Erro ao criar carteira</string>
<string name="generate_restoreheight_error">Escreva a altura ou data (AAAA-MM-DD)</string>
@ -274,7 +266,6 @@
<string name="tx_pending">PENDENTE</string>
<string name="tx_failed">FALHOU</string>
<string name="receive_paymentid_hint">ID do Pagamento (opcional)</string>
<string name="receive_amount_hint">Valor</string>
<string name="receive_cannot_open">Não foi possível abrir a carteira!</string>
<string name="receive_paymentid_invalid">16 ou 64 caracteres em hexadecimal (0&#8211;9,a&#8211;f)</string>
@ -284,8 +275,6 @@
<string name="receive_amount_negative">Mín. 0</string>
<string name="receive_amount_nan">O valor não é numérico</string>
<string name="receive_title">Receber</string>
<string name="details_alert_message">Dados importantes serão exibidos.\nCertifique-se que ninguém está espiando!</string>
<string name="details_alert_yes">Estou seguro</string>
<string name="details_alert_no">Tire-me daqui!</string>
@ -295,20 +284,12 @@
<string name="archive_alert_yes">Sim, pode fazer!</string>
<string name="archive_alert_no">Não, obrigado!</string>
<string-array name="priority">
<item>Prioridade padrão</item>
<item>Prioridade baixa</item>
<item>Prioridade média</item>
<item>Prioridade alta</item>
</string-array>
<string name="fab_create_new">Criar nova carteira</string>
<string name="fab_restore_viewonly">Restaurar carteira \"Somente leitura\"</string>
<string name="fab_restore_key">Restaurar carteira via chaves privadas</string>
<string name="fab_restore_seed">Restaurar carteira via semente mnemônica</string>
<string name="accounts_drawer_new">Criar conta</string>
<string name="accounts_drawer_title">Contas</string>
<string name="accounts_new">Conta adicionada #%1$d</string>
<string name="tx_account">Conta #</string>
@ -330,7 +311,6 @@
<string name="open_wallet_ledger_missing">Favor (re)conectar a Ledger</string>
<string name="accounts_progress_new">Criando conta</string>
<string name="accounts_progress_update">Atualizando carteira</string>
<string name="toast_ledger_attached">%1$s conectado</string>
<string name="toast_ledger_detached">%1$s desconectado</string>
@ -347,5 +327,31 @@
<string name="menu_info">Detalhes</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Feito</string>
<string name="label_receive_info_gen_qr_code">Toca para código QR</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_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[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Rácio: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avançado:</string>
<string name="label_send_btc_xmrto_info">Vai a xmr.to para suporte &amp; seguimento</string>
<string name="label_send_btc_xmrto_key_lb">Chave secreta\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Chave secreta</string>
@ -95,7 +91,6 @@
<string name="backup_progress">A efectuar cópia de segurança</string>
<string name="archive_progress">A arquivar</string>
<string name="rename_progress">A renomear</string>
<string name="open_progress">A verificar a conecção</string>
<string name="changepw_progress">A altear a palavra passe</string>
<string name="service_progress">A concluir o processamento &#8230;\nIsto pode demorar!</string>
@ -107,13 +102,11 @@
<string name="changepw_success">Palavra passe alterada</string>
<string name="label_daemon"></string>
<string name="prompt_daemon">([&lt;utilizador&gt;:&lt;passe&gt;@]&lt;serviço&gt;[:&lt;porta&gt;])</string>
<string name="status_wallet_loading">A carregar a carteira &#8230;</string>
<string name="status_wallet_unloaded">Carteira guardada</string>
<string name="status_wallet_unload_failed">Erro ao gravar a carteira!</string>
<string name="status_wallet_connecting">A conectar &#8230;</string>
<string name="status_wallet_connect_failed">Erro ao conectar!\nConfirma o utilizador/palavra passe</string>
<string name="status_wallet_connect_timeout">O tempo para a conecção expirou!\nTenta novamente ou outro nó.</string>
<string name="status_wallet_node_invalid">Nó inválido!\nTenta outro.</string>
<string name="status_wallet_connect_ioex">Não foi possível chegar ao nó!\nTenta novamente ou outro.</string>
@ -193,7 +186,6 @@
<string name="generate_wallet_dot">Não pode começar com .</string>
<string name="generate_wallet_creating">A criar carteira</string>
<string name="generate_wallet_created">Carteira criada</string>
<string name="generate_wallet_create_failed">Erro ao criar a carteira</string>
<string name="generate_restoreheight_error">Introduzir número ou data (AAAA-MM-DD)</string>
@ -273,7 +265,6 @@
<string name="tx_pending">PENDENTE</string>
<string name="tx_failed">FALHADA</string>
<string name="receive_paymentid_hint">ID Pagamento (opcional)</string>
<string name="receive_amount_hint">Quantidade</string>
<string name="receive_cannot_open">Não foi possível abrir a carteira!</string>
<string name="receive_paymentid_invalid">16 ou 64 caracteres em hexadecimal (0&#8211;9,a&#8211;f)</string>
@ -283,8 +274,6 @@
<string name="receive_amount_negative">Mín. 0</string>
<string name="receive_amount_nan">XMR não é um número</string>
<string name="receive_title">Receber</string>
<string name="details_alert_message">Dados sensíveis vão ser mostrados.\nOlha à tua volta!</string>
<string name="details_alert_yes">Estou seguro</string>
<string name="details_alert_no">Volta atrás!</string>
@ -294,13 +283,6 @@
<string name="archive_alert_yes">Sim, faz isso!</string>
<string name="archive_alert_no">Não obrigado!</string>
<string-array name="priority">
<item>Prioridade por Defeito</item>
<item>Prioridade Baixa</item>
<item>Prioridade Média</item>
<item>Prioridade Alta</item>
</string-array>
<string name="fab_create_new">Criar nova carteira</string>
<string name="fab_restore_viewonly">Restaurar carteira apenas de visualização</string>
<string name="fab_restore_key">Restaurar carteira a partir de chaves privadas</string>
@ -310,7 +292,6 @@
<string name="prompt_open_wallet">Opening the wallet&#8230;</string>
<string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string>
@ -332,7 +313,6 @@
<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>
@ -359,5 +339,31 @@
<string name="menu_info">Detalhes</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -28,10 +28,8 @@
<string name="label_send_done">Gata</string>
<string name="label_receive_info_gen_qr_code">Atinge pentru codul QR</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_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[
@ -78,8 +76,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Rată: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avansat:</string>
<string name="label_send_btc_xmrto_info">Vizitează xmr.to pentru suport &amp; interogare</string>
<string name="label_send_btc_xmrto_key_lb">Cheia secretă \nXMR.TO</string>
<string name="label_send_btc_xmrto_key">Cheia secretă XMR.TO </string>
@ -94,7 +90,6 @@
<string name="backup_progress">Copie de rezervă în curs</string>
<string name="archive_progress">Arhivare în curs</string>
<string name="rename_progress">Redenumire în curd</string>
<string name="open_progress">Se verifică conexiunea cu nodul</string>
<string name="service_progress">Impachetăm lucrurile &#8230;\nPoate dura un pic!</string>
@ -103,13 +98,11 @@
<string name="rename_failed">Redenumirea a eșuat!</string>
<string name="label_daemon">Nodul</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Se încarcă portofelul &#8230;</string>
<string name="status_wallet_unloaded">Portofelul a fost salvat</string>
<string name="status_wallet_unload_failed">Salvarea portofelului a eșuat!</string>
<string name="status_wallet_connecting">Se conecteaza &#8230;</string>
<string name="status_wallet_connect_failed">Conexiunea la nod a eșuat!\nVerifică utilizator/parolă</string>
<string name="status_wallet_connect_timeout">Conexiunea la nod a expirat!\nÎncearca din nou sau un altul.</string>
<string name="status_wallet_node_invalid">Nodul este invalid!\nÎncearcă altul.</string>
<string name="status_wallet_connect_ioex">Nu se poate ajunge la nod!\nÎncearca din nou sau un altul.</string>
@ -169,7 +162,6 @@
<string name="generate_wallet_dot">Nu pot începe cu .</string>
<string name="generate_wallet_creating">Se creează portofel</string>
<string name="generate_wallet_created">Portofel creat</string>
<string name="generate_wallet_create_failed">Crearea portofelului nereușită</string>
<string name="generate_restoreheight_error">Introdu număr sau dată (YYYY-MM-ZZ)</string>
@ -248,7 +240,6 @@
<string name="tx_pending">ÎN CURS</string>
<string name="tx_failed">EȘUAT</string>
<string name="receive_paymentid_hint">Payment ID (facultativ)</string>
<string name="receive_amount_hint">Sumă</string>
<string name="receive_cannot_open">Nu pot deschide portofel!</string>
<string name="receive_paymentid_invalid">16 sau 64 caractere Hex (0&#8211;9,a&#8211;f)</string>
@ -258,8 +249,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR fără valoare</string>
<string name="receive_title">Primește</string>
<string name="details_alert_message">Acum vor fi afișate date sensibile.\nUită-te peste umărul tău!</string>
<string name="details_alert_yes">Sunt în siguranță</string>
<string name="details_alert_no">Du-mă înapoi!</string>
@ -269,13 +258,6 @@
<string name="archive_alert_yes">Da, fă asta!</string>
<string name="archive_alert_no">Nu mersi!</string>
<string-array name="priority">
<item>Prioritate Implicită</item>
<item>Prioritate mică</item>
<item>Prioritate medie</item>
<item>Prioritate mare</item>
</string-array>
<string name="fab_create_new">Creeaza portofel nou</string>
<string name="fab_restore_viewonly">Restaurează portofel-vizualizare</string>
<string name="fab_restore_key">Restaurează portofel folosind cheie privată</string>
@ -308,7 +290,6 @@
<string name="generate_empty_passwordB">Passphrase may not be empty</string>
<string name="generate_crazypass_label">Wallet Files Restore Password</string>
<string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string>
@ -329,7 +310,6 @@
<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>
@ -356,5 +336,31 @@
<string name="menu_info">Detalii</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Готово</string>
<string name="label_receive_info_gen_qr_code">Нажмите для использования QR-кода</string>
<string name="info_send_prio_fees">Высокий приоритет = Высокие комиссии</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[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Курс: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Дополнительно:</string>
<string name="label_send_btc_xmrto_info">Посетите xmr.to для получения помощи &amp;</string>
<string name="label_send_btc_xmrto_key_lb">Секретный ключ\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO секретный ключ</string>
@ -96,7 +92,6 @@
<string name="backup_progress">Выполняется резервное копирование</string>
<string name="archive_progress">Выполняется архивирование</string>
<string name="rename_progress">Выполняется переименование</string>
<string name="open_progress">Проверка подключения</string>
<string name="changepw_progress">Выполняется изменение пароля</string>
<string name="service_progress">Для данного действия &#8230;\nМожет потребоваться некоторое время!</string>
@ -108,13 +103,11 @@
<string name="changepw_success">Изменение пароля выполено успешно</string>
<string name="label_daemon">Удаленный узел</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Загрузка кошелека &#8230;</string>
<string name="status_wallet_unloaded">Кошелек записан</string>
<string name="status_wallet_unload_failed">Ошибка записи кошелека!</string>
<string name="status_wallet_connecting">Подключение &#8230;</string>
<string name="status_wallet_connect_failed">Ошибка подключения к удаленному узлу!\nПроверьте username/password</string>
<string name="status_wallet_connect_timeout">Время ожидания соединения превышено!\nПопробуйте еще раз.</string>
<string name="status_wallet_node_invalid">Ошибка удаленного узла!\nПопробуйте еще раз.</string>
<string name="status_wallet_connect_ioex">Не удается подключится к удаленному узлу!\nПопробуйте позже.</string>
@ -195,7 +188,6 @@
<string name="generate_wallet_dot">Не может начинаться с .</string>
<string name="generate_wallet_creating">Создание кошелька</string>
<string name="generate_wallet_created">Кошелек создан</string>
<string name="generate_wallet_create_failed">Не удалось создать кошелек</string>
<string name="generate_restoreheight_error">Введите номер блока или дату (YYYY-MM-DD)</string>
@ -275,7 +267,6 @@
<string name="tx_pending">ОЖИДАНИЕ</string>
<string name="tx_failed">ОШИБКА</string>
<string name="receive_paymentid_hint">ID платежа (необязательно)</string>
<string name="receive_amount_hint">Сумма</string>
<string name="receive_cannot_open">Не удалось открыть кошелек!</string>
<string name="receive_paymentid_invalid">Система счисления - 16 или 64(0&#8211;9,a&#8211;f)</string>
@ -285,8 +276,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">Не числовое значение XMR</string>
<string name="receive_title">Получить</string>
<string name="details_alert_message">Сейчас будут показаны конфиденциальные данные. Оглянись вокруг!</string>
<string name="details_alert_yes">Я в безопасности</string>
<string name="details_alert_no">Верните меня обратно!</string>
@ -296,20 +285,12 @@
<string name="archive_alert_yes">Да, сделай это!</string>
<string name="archive_alert_no">Нет, спасибо!</string>
<string-array name="priority">
<item>Приоритет - Стандартный</item>
<item>Приоритет - Низкий</item>
<item>Приоритет - Средний</item>
<item>Приоритет - Высокий</item>
</string-array>
<string name="fab_create_new">Создать новый кошелек</string>
<string name="fab_restore_viewonly">Восстановить кошелек только для просмотра</string>
<string name="fab_restore_key">Восстановить кошелек из ключей</string>
<string name="fab_restore_seed">Восстановить кошелек из мнемонической фразы</string>
<string name="accounts_drawer_new">Создать учетную запись</string>
<string name="accounts_drawer_title">Учетная запись</string>
<string name="accounts_new">Добавить новую учетную запись #%1$d</string>
<string name="tx_account">Учетная запись #</string>
@ -331,7 +312,6 @@
<string name="open_wallet_ledger_missing">Подключите (переподключите) устройство Ledger</string>
<string name="accounts_progress_new">Создание аккаунта</string>
<string name="accounts_progress_update">Обновление кошелька</string>
<string name="toast_ledger_attached">%1$s прикреплен</string>
<string name="toast_ledger_detached">%1$s откреплён</string>
@ -358,5 +338,31 @@
<string name="menu_info">Информация</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">Hotovo</string>
<string name="label_receive_info_gen_qr_code">Klepni pre QR kód</string>
<string name="info_send_prio_fees">Vyššia priorita = Vyšší poplatok</string>
<string name="info_xmrto_enabled">BTC platby aktivované, klepni pre viac info.</string>
<string name="info_crazypass_enabled">CrAzYpass aktivovaný, klepni pre viac info.</string>
<string name="info_ledger_enabled">Ledger aktivovaný, klepni pre viac info.</string>
<string name="info_xmrto"><![CDATA[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Kurz: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Pokročilé:</string>
<string name="label_send_btc_xmrto_info">Navštív XMR.TO pre podporu &amp; tracking</string>
<string name="label_send_btc_xmrto_key_lb">Tajný kľúč\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Tajný kľúč</string>
@ -96,7 +92,6 @@
<string name="backup_progress">Prebieha Záloha</string>
<string name="archive_progress">Prebieha Archivácia</string>
<string name="rename_progress">Prebieha Premenovanie</string>
<string name="open_progress">Skúšam spojenie so serverom</string>
<string name="changepw_progress">Prebieha zmena hesla</string>
<string name="service_progress">Balím si veci &#8230;\nMôže to chvíľu trvať!</string>
@ -108,13 +103,11 @@
<string name="changepw_success">Heslo zmenené</string>
<string name="label_daemon">Uzol</string>
<string name="prompt_daemon">([&lt;meno&gt;:&lt;heslo&gt;@]&lt;server&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Načítavam peňaženku &#8230;</string>
<string name="status_wallet_unloaded">Peňaženka uložená</string>
<string name="status_wallet_unload_failed">Uloženie zlyhalo!</string>
<string name="status_wallet_connecting">pripájam sa &#8230;</string>
<string name="status_wallet_connect_failed">Spojenie s uzlom zlyhalo!\nSkontroluj užívateľa/heslo</string>
<string name="status_wallet_connect_timeout">Pripojenie uzla vypršalo!\nSkús znova, alebo skús iný uzol.</string>
<string name="status_wallet_node_invalid">Neplatný uzol!\nSkús iný.</string>
<string name="status_wallet_connect_ioex">Neviem sa spojiť s uzlom!\nSkús znova, alebo skús iný uzol.</string>
@ -192,7 +185,6 @@
<string name="generate_wallet_dot">Neslobodno začať s .</string>
<string name="generate_wallet_creating">Vytváranie peňaženky</string>
<string name="generate_wallet_created">Peňaženka vytvorená</string>
<string name="generate_wallet_create_failed">Vytvorenie peňaženky zlyhalo</string>
<string name="generate_restoreheight_error">Vlož číslo alebo dátum (YYYY-MM-DD)</string>
@ -272,7 +264,6 @@
<string name="tx_pending">ČAKAJÚCI</string>
<string name="tx_failed">ZLYHAL</string>
<string name="receive_paymentid_hint">ID Platby (voliteľné)</string>
<string name="receive_amount_hint">Suma</string>
<string name="receive_cannot_open">Nemohol som otvoriť peňaženku!</string>
<string name="receive_paymentid_invalid">16 alebo 64 hexa znakov (0&#8211;9,a&#8211;f)</string>
@ -282,8 +273,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR nie číslo</string>
<string name="receive_title">Prijať</string>
<string name="details_alert_message">Budú zobrazené citlivé dáta.\nPozri cez plece!</string>
<string name="details_alert_yes">Som v bezpečí</string>
<string name="details_alert_no">Naspäť!</string>
@ -293,20 +282,12 @@
<string name="archive_alert_yes">Áno, poďme na to!</string>
<string name="archive_alert_no">Nie, díky!</string>
<string-array name="priority">
<item>Priorita Štandard</item>
<item>Priorita Nízka</item>
<item>Priorita Medium</item>
<item>Priorita Vysoká</item>
</string-array>
<string name="fab_create_new">Vytvor novú peňaženku</string>
<string name="fab_restore_viewonly">Obnoviť prezeraciu peňaženku</string>
<string name="fab_restore_key">Obnoviť peňaženku zo súkromných kľúčov</string>
<string name="fab_restore_seed">Obnoviť peňaženku z 25-slovného seedu</string>
<string name="accounts_drawer_new">Vytvor Účet</string>
<string name="accounts_drawer_title">Účty</string>
<string name="accounts_new">Pridaný nový účet #%1$d</string>
<string name="tx_account">Účet #</string>
@ -328,7 +309,6 @@
<string name="open_wallet_ledger_missing">(znovu) zapoj Ledger</string>
<string name="accounts_progress_new">Vytváram účet</string>
<string name="accounts_progress_update">Aktualizujem peňaženku</string>
<string name="toast_ledger_attached">%1$s pripojený</string>
<string name="toast_ledger_detached">%1$s odpojený</string>
@ -350,11 +330,36 @@
<string name="send_address_resolve_openalias">Prekladám OpenAlias&#8230;</string>
<string name="send_address_no_dnssec">OpenAlias bez DNSSEC - adresa môže byť zneužitá</string>
<string name="send_address_hint">Prijímateľova XMR/BTC adresa alebo OpenAlias</string>
<string name="info_v9" translatable="false">vyžaduje sa uzol V9 </string>
<string name="status_wallet_connect_wrongversion">Nekompatibilný s verziou uzla - nutná aktualizácia!</string>
<string name="menu_info">Detaily</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -30,10 +30,8 @@
<string name="label_send_done">Färdig</string>
<string name="label_receive_info_gen_qr_code">Tryck för QR-kod</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_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>
@ -72,8 +70,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Kurs: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Avancerat:</string>
<string name="label_send_btc_xmrto_info">Besök xmr.to för support &amp; spårning</string>
<string name="label_send_btc_xmrto_key_lb">Hemlig nyckel\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO hemlig nyckel</string>
@ -88,7 +84,6 @@
<string name="backup_progress">Säkerhetskopiering pågår</string>
<string name="archive_progress">Arkivering pågår</string>
<string name="rename_progress">Namnbyte pågår</string>
<string name="open_progress">Kontrollerar anslutning till daemon</string>
<string name="changepw_progress">Ändring av lösenord pågår</string>
<string name="service_progress">Gör klart några saker …\nDetta kan ta lite tid!</string>
@ -100,13 +95,11 @@
<string name="changepw_success">Lösenordet har ändrats</string>
<string name="label_daemon">Nod</string>
<string name="prompt_daemon">([&lt;användare&gt;:&lt;lösenord&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">Läser in plånbok …</string>
<string name="status_wallet_unloaded">Plånbok sparad</string>
<string name="status_wallet_unload_failed">Det gick inte att spara plånbok!</string>
<string name="status_wallet_connecting">Ansluter …</string>
<string name="status_wallet_connect_failed">Kunde inte ansluta till nod!\nKontrollera användarnamn/lösenord</string>
<string name="status_wallet_connect_timeout">Fel: Anslutning till nod uppnådde en tidsgräns!\nFörsök igen eller en annan nod.</string>
<string name="status_wallet_node_invalid">Ogiltig nod!\nPröva någon annan.</string>
<string name="status_wallet_connect_ioex">Noden kan inte nås!\nFörsök igen eller en annan nod.</string>
@ -174,7 +167,6 @@
<string name="generate_wallet_dot">Får inte börja med .</string>
<string name="generate_wallet_creating">Skapar plånbok</string>
<string name="generate_wallet_created">Plånboken skapades</string>
<string name="generate_wallet_create_failed">Det gick inte att skapa plånbok</string>
<string name="generate_restoreheight_error">Ange ett tal eller ett datum (ÅÅÅÅ-MM-DD)</string>
@ -254,7 +246,6 @@
<string name="tx_pending">VÄNTANDE</string>
<string name="tx_failed">MISSLYCKAD</string>
<string name="receive_paymentid_hint">Betalnings-ID (valfritt)</string>
<string name="receive_amount_hint">Belopp</string>
<string name="receive_cannot_open">Det gick inte att öppna plånboken!</string>
<string name="receive_paymentid_invalid">16 eller 64 hexadecimala tecken (09, af)</string>
@ -264,8 +255,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR är inte ett tal</string>
<string name="receive_title">Ta emot</string>
<string name="details_alert_message">Nu kommer känsliga data att visas.\nTitta över axeln!</string>
<string name="details_alert_yes">Det är OK</string>
<string name="details_alert_no">Ta mig tillbaka!</string>
@ -275,13 +264,6 @@
<string name="archive_alert_yes">Ja, gör det!</string>
<string name="archive_alert_no">Nej tack!</string>
<string-array name="priority">
<item>Prioritet standard</item>
<item>Prioritet låg</item>
<item>Prioritet medel</item>
<item>Prioritet hög</item>
</string-array>
<string name="fab_create_new">Skapa ny plånbok</string>
<string name="fab_restore_viewonly">Återställ granskningsplånbok</string>
<string name="fab_restore_key">Återställ plånbok från privata nycklar</string>
@ -291,7 +273,6 @@
<string name="bad_saved_password">Det sparade lösenordet är inkorrekt.\nSkriv lösenordet manuellt.</string>
<string name="accounts_drawer_new">Skapa konto</string>
<string name="accounts_drawer_title">Konton</string>
<string name="accounts_new">Nytt konto skapat #%1$d</string>
<string name="tx_account">Konto #</string>
@ -313,7 +294,6 @@
<string name="open_wallet_ledger_missing">Vänligen (åter)anslut Ledger</string>
<string name="accounts_progress_new">Skapar konto</string>
<string name="accounts_progress_update">Uppdaterar blånbok</string>
<string name="toast_ledger_attached">%1$s ansluten</string>
<string name="toast_ledger_detached">%1$s frånkopplad</string>
@ -340,5 +320,31 @@
<string name="menu_info">Detaljer</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">完成</string>
<string name="label_receive_info_gen_qr_code">点选显示QR码</string>
<string name="info_send_prio_fees">较高优先权 = 较高手续费</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[
@ -79,8 +77,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(汇率: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">高级选项:</string>
<string name="label_send_btc_xmrto_info">参访 xmr.to 以获得支援及追踪交易</string>
<string name="label_send_btc_xmrto_key_lb">私钥\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO 私钥</string>
@ -95,7 +91,6 @@
<string name="backup_progress">备份中</string>
<string name="archive_progress">封存中</string>
<string name="rename_progress">重新命名中</string>
<string name="open_progress">检查背景程序连网中</string>
<string name="changepw_progress">更改密码中</string>
<string name="service_progress">处理中 &#8230;\n可能会花费一点时间</string>
@ -107,13 +102,11 @@
<string name="changepw_success">密码更改成功</string>
<string name="label_daemon">节点</string>
<string name="prompt_daemon">([&lt;使用者&gt;:&lt;密码&gt;@]&lt;位置&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">载入钱包中 &#8230;</string>
<string name="status_wallet_unloaded">钱包已储存</string>
<string name="status_wallet_unload_failed">储存钱包失败!</string>
<string name="status_wallet_connecting">连接中 &#8230;</string>
<string name="status_wallet_connect_failed">连接至节点失败!\n请检查使用者/密码。</string>
<string name="status_wallet_connect_timeout">节点连接逾时!\n请重试或更換节点。</string>
<string name="status_wallet_node_invalid">节点无效!\n请试试其他节点。</string>
<string name="status_wallet_connect_ioex">无法连接至节点!\n请试试其他节点。</string>
@ -191,7 +184,6 @@
<string name="generate_wallet_dot">无法使用.作为开头</string>
<string name="generate_wallet_creating">建立钱包</string>
<string name="generate_wallet_created">钱包已建立</string>
<string name="generate_wallet_create_failed">钱包建立失败</string>
<string name="generate_restoreheight_error">输入区块高度或日期(YYYY-MM-DD)</string>
@ -271,7 +263,6 @@
<string name="tx_pending">等待确认中</string>
<string name="tx_failed">失败</string>
<string name="receive_paymentid_hint">付款ID (选填)</string>
<string name="receive_amount_hint">金额</string>
<string name="receive_cannot_open">无法开启钱包!</string>
<string name="receive_paymentid_invalid">16 或 64 Hex 字元 (0&#8211;9,a&#8211;f)</string>
@ -281,8 +272,6 @@
<string name="receive_amount_negative">最小值 0</string>
<string name="receive_amount_nan">输入的XMR不是数字</string>
<string name="receive_title">接收</string>
<string name="details_alert_message">将会选示敏感信息。\n请注意周遭安全!</string>
<string name="details_alert_yes">我现在很安全</string>
<string name="details_alert_no">不看了!</string>
@ -292,20 +281,12 @@
<string name="archive_alert_yes">好的!</string>
<string name="archive_alert_no">不了!</string>
<string-array name="priority">
<item>预设优先权</item>
<item>低优先权</item>
<item>中优先权</item>
<item>高优先权</item>
</string-array>
<string name="fab_create_new">建立新钱包</string>
<string name="fab_restore_viewonly">恢复只读钱包</string>
<string name="fab_restore_key">从私钥恢复钱包</string>
<string name="fab_restore_seed">从25字种子码恢复钱包</string>
<string name="accounts_drawer_new">开新户口</string>
<string name="accounts_drawer_title">户口</string>
<string name="accounts_new">已新增户口 #%1$d</string>
<string name="tx_account">户口 #</string>
@ -327,7 +308,6 @@
<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>
@ -354,5 +334,31 @@
<string name="menu_info">详细信息</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -29,10 +29,8 @@
<string name="label_send_done">完成</string>
<string name="label_receive_info_gen_qr_code">點選顯示 QR 碼</string>
<string name="info_send_prio_fees">較高優先權 = 較高手續費</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[
@ -80,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(匯率:%1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">進階:</string>
<string name="label_send_btc_xmrto_info">參訪 XMR.TO 以獲得支援及追蹤交易</string>
<string name="label_send_btc_xmrto_key_lb">私鑰\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO 私鑰</string>
@ -96,7 +92,6 @@
<string name="backup_progress">備份中</string>
<string name="archive_progress">封存中</string>
<string name="rename_progress">重新命名中</string>
<string name="open_progress">檢查背景程式連線中</string>
<string name="changepw_progress">更改密碼中</string>
<string name="service_progress">處理中 &#8230;\n可能需要些許時間</string>
@ -108,13 +103,11 @@
<string name="changepw_success">密碼更改成功</string>
<string name="label_daemon">節點</string>
<string name="prompt_daemon">([&lt;使用者&gt;:&lt;密碼&gt;@]&lt;位置&gt;[:&lt;port&gt;])</string>
<string name="status_wallet_loading">載入錢包中 &#8230;</string>
<string name="status_wallet_unloaded">錢包已儲存</string>
<string name="status_wallet_unload_failed">儲存錢包失敗!</string>
<string name="status_wallet_connecting">連接中 &#8230;</string>
<string name="status_wallet_connect_failed">連接至節點失敗!\n請檢查使用者/密碼。</string>
<string name="status_wallet_connect_timeout">節點連接逾時!\n請重試或更換節點。</string>
<string name="status_wallet_node_invalid">節點無效!\n請試試其他節點。</string>
<string name="status_wallet_connect_ioex">無法連接至節點!\n請試試其他節點。</string>
@ -192,7 +185,6 @@
<string name="generate_wallet_dot">無法用.作為開頭</string>
<string name="generate_wallet_creating">建立錢包</string>
<string name="generate_wallet_created">錢包已建立</string>
<string name="generate_wallet_create_failed">錢包建立失敗</string>
<string name="generate_restoreheight_error">輸入區塊高度或日期 (YYYY-MM-DD)</string>
@ -272,7 +264,6 @@
<string name="tx_pending">等待確認中</string>
<string name="tx_failed">失敗</string>
<string name="receive_paymentid_hint">付款 ID (選填)</string>
<string name="receive_amount_hint">金額</string>
<string name="receive_cannot_open">無法開啟錢包!</string>
<string name="receive_paymentid_invalid">16 或 64 Hex 字元 (0&#8211;9,a&#8211;f)</string>
@ -282,8 +273,6 @@
<string name="receive_amount_negative">最小值 0</string>
<string name="receive_amount_nan">輸入的 XMR 不是數字</string>
<string name="receive_title">接收</string>
<string name="details_alert_message">將會顯示敏感資訊。\n請注意周遭安全</string>
<string name="details_alert_yes">我現在很安全</string>
<string name="details_alert_no">不看了!</string>
@ -293,20 +282,12 @@
<string name="archive_alert_yes">好的!</string>
<string name="archive_alert_no">不了!</string>
<string-array name="priority">
<item>預設優先權</item>
<item>低優先權</item>
<item>中優先權</item>
<item>高優先權</item>
</string-array>
<string name="fab_create_new">建立新錢包</string>
<string name="fab_restore_viewonly">回復唯讀錢包</string>
<string name="fab_restore_key">從私鑰回復錢包</string>
<string name="fab_restore_seed">從 25 字種子碼回復錢包</string>
<string name="accounts_drawer_new">新增帳戶</string>
<string name="accounts_drawer_title">帳戶</string>
<string name="accounts_new">已新增帳戶 #%1$d</string>
<string name="tx_account">帳戶 #</string>
@ -328,7 +309,6 @@
<string name="open_wallet_ledger_missing">請(重新)連接 Ledger 裝置</string>
<string name="accounts_progress_new">正在建立帳戶</string>
<string name="accounts_progress_update">正在更新帳戶</string>
<string name="toast_ledger_attached">%1$s 已連接</string>
<string name="toast_ledger_detached">%1$s 已斷開連接</string>
@ -355,5 +335,31 @@
<string name="menu_info">詳細資訊</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -216,16 +216,6 @@
previous step and then coming back to the \"Confirm\" screen.</p>
]]></string>
<string name="help_v9" translatable="false"><![CDATA[
<h1>October 2018 Network Upgrade</h1>
<p>On or around 18th October 2018, the Monero Network will be upgraded. Clients and Servers
(Nodes) need to be upgraded simultaneously as old & new versions are not compatible.</p>
<p>You are running a new V9 client. You need to run or find a V9 node to connect to.</p>
<p>Check
https://www.reddit.com/r/Monerujo/comments/9n70ys/does_anyone_have_a_list_of_updated_nodes/
for more info &amp; support.</p>
]]></string>
<string name="help_wallet"><![CDATA[
<h1>The Wallet</h1>
<h2>Street Mode</h2>
@ -247,4 +237,53 @@
<h2>Transaction List</h2>
<p>A list of the wallet transactions. In view wallets, only incoming transactions are shown.</p>
]]></string>
<string name="help_node" translatable="false"><![CDATA[
<h1>Nodes</h1>
<h2>TL;DR</h2>
<p>Refresh the nodes list by pulling down &amp; bookmark 3&#8211;5 nodes to allow Monerujo
to choose the best one for you!</p>
<h2>What&apos;s a Node?</h2>
<p>Monerujo uses a Remote Node (sometimes also called Daemon) to communicate with
the Monero Network without having to download and store a copy of the
whole blockchain itself.<p>
<h2>Node List</h2>
<p>If the list is empty, you can either add new nodes manually or let Monerujo
scan the network for you. Or both. Read on&#8230;</p>
<p>The node list shows all currently known nodes. Additionally, the timestamp
of the latest block known to each node is shown under the node name. An icon
representing the node&apos;s response behaviour
(which indicates the level of connectivity to be expected)
is shown next to each node.</p>
<p>Any node in the list can be bookmarked for later use.
Nodes which are not bookmarked will be forgotten.<p>
<p>Monerujo will choose the optimal bookmarked node each time you use it.
It does this by checking the blockheight (how up-to-date
is the node?) as well as the response behaviour (how fast does the node respond to requests?).</p>
<p>The list is sorted by these characteristics, so the top node would be the one Monerujo
would choose right now. The bottom of the list would show very slow or unavailable nodes.</p>
<h2>Add a Node</h2>
<p>By touching the &quot;Add Node&quot; button at the bottom, you will be asked to
enter the node details in the following dialog.
The &quot;Address&quot; is the hostname or IP-address of the node - this is the only
mandatory entry.
Enter the &quot;Port&quot; if the node runs on a non-default port (e.g. 18089).
You can also optionally name the node, so you can identify it easier later on.
Some nodes require credentials to use them. Enter the provided username &amp;
password in the appropriate fields. Now you can &quot;Test&quot; these setting.
The &quot;Test Results&quot; will display the blockheight, response time and actual IP used.
The result may also be an error - usually because the hostname provided is
not reachable in a sensible amount of time or the credentials are incorrect.
Or the hostname/port combination does not point to an actual Monero Node!
Once the test passes (no error) - you&apos;re set to press &quot;OK&quot; to save &amp;
bookmark this node.</p>
<h2>Scan for Nodes</h2>
<p>Additionally, you can scan the network for nodes. Monerujo will start
scanning the network for Remote Nodes on port 18089. It begins by asking your
bookmarked nodes for other peers in the Monero P2P network and then continues
by asking those for their peers, and so on. If you have no bookmarked nodes
(or they don&apos;t tell us about their peers),
Monerujo will go straight to the Monero seed nodes hardcoded into Monero. The
scan stops when it finds 10 remote nodes in total.</p>
]]></string>
</resources>

@ -2,7 +2,6 @@
<string name="app_name" translatable="false">monerujo</string>
<string name="wallet_activity_name">Wallet</string>
<string name="menu_stagenet" translatable="false">Stagenet</string>
<string name="menu_about">About</string>
<string name="menu_privacy">Privacy Policy</string>
@ -31,10 +30,8 @@
<string name="label_send_done">Done</string>
<string name="label_receive_info_gen_qr_code">Touch for QR Code</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_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[
@ -81,8 +78,6 @@
<string name="text_send_btc_amount">%1$s BTC = %2$s XMR</string>
<string name="text_send_btc_rate">(Rate: %1$s BTC/XMR)</string>
<string name="label_send_settings_advanced">Advanced:</string>
<string name="label_send_btc_xmrto_info">Visit xmr.to for support &amp; tracking</string>
<string name="label_send_btc_xmrto_key_lb">Secret Key\nXMR.TO</string>
<string name="label_send_btc_xmrto_key">XMR.TO Secret Key</string>
@ -97,7 +92,6 @@
<string name="backup_progress">Backup in progress</string>
<string name="archive_progress">Archive in progress</string>
<string name="rename_progress">Rename in progress</string>
<string name="open_progress">Checking daemon connection</string>
<string name="changepw_progress">Change Password in progress</string>
<string name="service_progress">Wrapping things up &#8230;\nThis can take a while!</string>
@ -109,17 +103,14 @@
<string name="changepw_success">Password changed</string>
<string name="label_daemon">Node</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="connect_stagenet" translatable="false">Stagenet</string>
<string name="connect_testnet" translatable="false">Testnet</string>
<string name="connect_mainnet" translatable="false">Mainnet</string>
<string name="status_wallet_loading">Loading Wallet &#8230;</string>
<string name="status_wallet_unloaded">Wallet saved</string>
<string name="status_wallet_unload_failed">Wallet save failed!</string>
<string name="status_wallet_connecting">Connecting &#8230;</string>
<string name="status_wallet_connect_failed">Node connection failed!\nCheck username/password</string>
<string name="status_wallet_connect_wrongversion">Node version incompatible - please upgrade!</string>
<string name="status_wallet_connect_timeout">Node connection timed out!\nTry again or another.</string>
<string name="status_wallet_node_invalid">Node invalid!\nTry another.</string>
<string name="status_wallet_connect_ioex">Cannot reach node!\nTry again or another.</string>
@ -143,7 +134,7 @@
<string name="bad_password">Incorrect password!</string>
<string name="bad_saved_password">Saved password is incorrect.\nPlease enter password manually.</string>
<string name="bad_wallet">Wallet does not exist!</string>
<string name="prompt_daemon_missing">Daemon address must be set!</string>
<string name="prompt_daemon_missing">Node must be set!</string>
<string name="prompt_wrong_net">Wallet does not match selected net</string>
<string name="label_watchonly">(Watch Only)</string>
@ -200,7 +191,6 @@
<string name="generate_wallet_dot">Cannot begin with .</string>
<string name="generate_wallet_creating">Creating wallet</string>
<string name="generate_wallet_created">Wallet created</string>
<string name="generate_wallet_create_failed">Wallet create failed</string>
<string name="generate_restoreheight_error">Enter Number or Date (YYYY-MM-DD)</string>
@ -287,7 +277,6 @@
<string name="tx_pending">PENDING</string>
<string name="tx_failed">FAILED</string>
<string name="receive_paymentid_hint">Payment ID (optional)</string>
<string name="receive_amount_hint">Amount</string>
<string name="receive_desc_hint">Description (optional)</string>
<string name="receive_cannot_open">Could not open wallet!</string>
@ -298,8 +287,6 @@
<string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR not a number</string>
<string name="receive_title">Receive</string>
<string name="details_alert_message">Sensitive data will now be shown.\nLook over your shoulder!</string>
<string name="details_alert_yes">I\'m safe</string>
<string name="details_alert_no">Take me back!</string>
@ -309,13 +296,6 @@
<string name="archive_alert_yes">Yes, do that!</string>
<string name="archive_alert_no">No thanks!</string>
<string-array name="priority">
<item>Priority Default</item>
<item>Priority Low</item>
<item>Priority Medium</item>
<item>Priority High</item>
</string-array>
<string-array name="currency" translatable="false">
<item>XMR</item>
<item>EUR</item>
@ -362,7 +342,6 @@
<string name="tx_account_formatted" translatable="false">(%1$d, %2$d)</string>
<string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string>
<string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string>
@ -384,7 +363,6 @@
<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>
@ -399,12 +377,37 @@
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
<string name="info_v9" translatable="false">V9 node required</string>
<string name="unknown_amount" translatable="false">\?\?\?</string>
<string name="text_streetmode" translatable="false">Street Mode</string>
<string name="menu_info">Show Secrets!</string>
<string name="menu_streetmode">Street Mode</string>
<string name="info_streetmode_enabled">Street Mode enabled, tap for more info.</string>
<string name="node_wrong_net" translatable="false">Node scanning only for mainnet!</string>
<string name="info_nodes_enabled">Node-o-matiC enabled, tap for more info.</string>
<string name="node_height">Last block updated: %1$s</string>
<string name="label_nodes">Nodes</string>
<string name="node_name_hint">Node Name (Optional)</string>
<string name="node_address_hint">Hostname</string>
<string name="node_port_hint">Port</string>
<string name="node_user_hint">Username (Optional)</string>
<string name="node_pass_hint">Password (Optional)</string>
<string name="node_host_unresolved">Cannot resolve host</string>
<string name="node_host_empty">We need this!</string>
<string name="node_port_numeric">Must be numeric</string>
<string name="node_port_range">Must be 1&#8211;65535</string>
<string name="node_fab_add">Add Node</string>
<string name="node_refresh_hint">Touch to refresh!</string>
<string name="node_test_error">CONNECTION ERROR %1$d</string>
<string name="node_general_error">CONNECTION ERROR</string>
<string name="node_auth_error">AUTHENTICATION FAILED</string>
<string name="node_result_label">Test Result:</string>
<string name="node_result">Height: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string>
<string name="node_testing">Testing IP: %1$s &#8230;</string>
<string name="node_refresh_wait">Please wait for scan to finish</string>
<string name="node_create_hint">Touch to select or add nodes</string>
<string name="node_pull_hint">Add nodes manually or pull down to scan</string>
<string name="node_scanning">Scanning network&#8230;</string>
<string name="node_nobookmark">Automatically bookmarked best %1$d nodes</string>
<string name="label_test">Test</string><!--note: as in "Test a network connection"-->
</resources>

@ -60,14 +60,6 @@
<item name="android:textColor">@color/moneroBlack</item>
</style>
<style name="MoneroLabel.Caps.Gray">
<item name="android:textColor">@color/moneroGray</item>
</style>
<style name="MoneroLabel.Caps.Gray.Small">
<item name="android:textSize">12sp</item>
</style>
<style name="MoneroText.Funds">
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/moneroFab</item>
@ -191,15 +183,6 @@
<item name="android:textStyle">bold</item>
</style>
<style name="MoneroText.Label.Heading">
<item name="android:textAllCaps">true</item>
<item name="android:textStyle">bold</item>
</style>
<style name="MoneroText.Label.Medium">
<item name="android:textSize">14sp</item>
</style>
<style name="MoneroText.Small">
<item name="android:textSize">10sp</item>
</style>
@ -208,10 +191,6 @@
<item name="android:textSize">12sp</item>
</style>
<style name="MoneroText.Medium.Bold">
<item name="android:textStyle">bold</item>
</style>
<style name="MoneroText.Large">
<item name="android:textSize">24sp</item>
</style>
@ -254,13 +233,6 @@
<item name="android:textSize">11sp</item>
</style>
<style name="MoneroButton.Alert">
<item name="android:textColor">@color/moneroBlack</item>
<item name="android:background">@color/gradientPink</item>
</style>
<style name="MoneroButton.ReallySend"></style>
<style name="MoneroButton.Take">
<item name="android:background">@color/take</item>
</style>
@ -274,31 +246,6 @@
<item name="android:backgroundTint">@color/gradientPink</item>
</style>
<style name="MoneroEdit.Amount">
<!--item name="android:textSize">32sp</item-->
<item name="android:textColor">@color/gradientOrange</item>
<item name="android:textStyle">bold</item>
</style>
<style name="MoneroEdit.Medium" parent="MoneroText.Medium">
<item name="android:textColor">?colorPrimary</item>
<item name="android:backgroundTint">@color/gradientPink</item>
</style>
<style name="MoneroEdit.Small">
<item name="android:textSize">14sp</item>
</style>
<style name="MoneroProgress" parent="@android:style/Widget.ProgressBar.Horizontal">
<item name="android:progressTint">?colorPrimaryDark</item>
<item name="android:progressBackgroundTint">?colorPrimary</item>
</style>
<style name="MoneroListView" parent="@android:style/Widget.ListView">
<item name="android:divider">@null</item>
<item name="android:dividerHeight">0dp</item>
</style>
<style name="MoneroSpinner" parent="@style/Widget.AppCompat.Spinner">
<item name="android:background">@drawable/backgound_spinner</item>
@ -318,15 +265,11 @@
</style>
<style name="MoneroTextAppearanceSpinnerItemGray" parent="android:TextAppearance.Widget.TextView.SpinnerItem">
<item name="android:textColor">@color/moneroGray</item>
</style>
<style name="MoneroSpinnerGray" parent="@style/Widget.AppCompat.Spinner">
<item name="android:background">@drawable/backgound_spinner_gray</item>
</style>
<style name="MoneroSpinnerItemGray" parent="android:Widget.TextView.SpinnerItem">
<item name="android:textAppearance">@style/MoneroTextAppearanceSpinnerItemGray</item>
<style name="ExtendedFAB" parent="MoneroButton">
<item name="android:textSize">16sp</item>
</style>
</resources>

@ -71,7 +71,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_shouldBeGetMethod()
throws InterruptedException, TimeoutException {
throws InterruptedException {
exchangeApi.queryExchangeRate("XMR", "EUR", mockExchangeCallback);
@ -81,7 +81,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_shouldHavePairInUrl()
throws InterruptedException, TimeoutException {
throws InterruptedException {
exchangeApi.queryExchangeRate("XMR", "EUR", mockExchangeCallback);
@ -91,7 +91,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_wasSuccessfulShouldRespondWithRate()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
final String base = "XMR";
final String quote = "EUR";
final double rate = 1.56;
@ -119,7 +119,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_wasSuccessfulShouldRespondWithRateUSD()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
final String base = "XMR";
final String quote = "USD";
final double rate = 1.56;
@ -147,7 +147,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_wasNotSuccessfulShouldCallOnError()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
exchangeApi.queryExchangeRate("XMR", "USD", new ExchangeCallback() {
@ -170,7 +170,7 @@ public class ExchangeRateTest {
@Test
public void queryExchangeRate_unknownAssetShouldCallOnError()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
MockResponse jsonMockResponse = new MockResponse().setBody(
createMockExchangeRateErrorResponse());
mockWebServer.enqueue(jsonMockResponse);

@ -124,8 +124,7 @@ public class RestoreHeightTest {
private boolean isInRange(long n, long min, long max) {
if (n > max) return false;
if (n < min) return false;
return true;
return n >= min;
}
private long getHeight(String date) {

@ -71,7 +71,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_shouldBePostMethod()
throws InterruptedException, TimeoutException {
throws InterruptedException {
xmrToApi.createOrder(0.5, "btcsomething", mockOrderXmrToCallback);
@ -81,7 +81,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_shouldBeContentTypeJson()
throws InterruptedException, TimeoutException {
throws InterruptedException {
xmrToApi.createOrder(0.5, "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW", mockOrderXmrToCallback);
@ -91,7 +91,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_shouldContainValidBody()
throws InterruptedException, TimeoutException {
throws InterruptedException {
final String validBody = "{\"btc_amount\":0.1,\"btc_dest_address\":\"19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW\"}";
@ -104,7 +104,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_wasSuccessfulShouldRespondWithOrder()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
final double amount = 1.23456789;
final String address = "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW";
final String uuid = "xmrto-abcdef";
@ -134,7 +134,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_wasNotSuccessfulShouldCallOnError()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
xmrToApi.createOrder(0.5, "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW", new XmrToCallback<CreateOrder>() {
@Override
@ -156,7 +156,7 @@ public class XmrToApiCreateOrderTest {
@Test
public void createOrder_malformedAddressShouldCallOnError()
throws InterruptedException, JSONException, TimeoutException {
throws TimeoutException {
mockWebServer.enqueue(new MockResponse().
setResponseCode(400).
setBody("{\"error_msg\":\"malformed bitcoin address\",\"error\":\"XMRTO-ERROR-002\"}"));

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

Loading…
Cancel
Save