1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-03 08:23:04 +02:00

Compare commits

...

45 Commits

Author SHA1 Message Date
m2049r
7fc2dc3ba1 bump version code 2018-08-08 22:10:39 +02:00
m2049r
43204d64ef Interim merge of Ledger translations (#383)
* Ledger french translation (#359)

* zh-TW translation for Ledger (#368, #373)

* Hungarian Ledgers strings added (#372)

* German translation

* update help about entering height
2018-08-08 22:10:28 +02:00
m2049r
1433143a39 Use NFC for receive & send (#380)
* Use NFC Tag for receive & send

* prep translations
2018-08-06 22:18:57 +02:00
m2049r
a9a78393a9 clear amounts when typing (#379) 2018-08-05 23:26:01 +02:00
m2049r
fe7ab31050 getRestoreHeight (#378) 2018-08-05 23:25:41 +02:00
m2049r
bf5ed793b3 Monerujo for Ledger Nano S (#377) 2018-08-04 01:03:26 +02:00
m2049r
679bae5f42 update de strings.xml (#376) 2018-08-02 11:11:57 +02:00
m2049r
e3ccda910e update hu translation (#375) 2018-08-02 11:05:09 +02:00
0140454
ae75a34977 Improve method of enumerating available locales (#374)
* Change method of enumerating translated locales
2018-08-02 10:51:10 +02:00
0140454
03efedf35c Implement language switcher (#344)
* Implement language switcher

* Use unique string resource to enumerate translated locale
2018-08-01 14:18:54 +02:00
m2049r
403dbdf14f update restoreheightdate handling (#370)
* restore date without dashes

* heights upated to august 2018
2018-08-01 08:25:25 +02:00
Attila
0bf3c6f099 Hungarian translation (#371) 2018-07-31 22:39:17 +02:00
0140454
1b0ac1c481 Show message for recoverable error encountered during authentication (#343) 2018-07-30 10:25:42 +02:00
uiharu
dc95539fc1 Update zh-rTW "strings.xml" (#336)
* Update zh-rTW "strings.xml

- translated new strings
- correction and editing

* Update strings.xml
2018-07-29 23:41:29 +02:00
0140454
023fb9e215 Wrap change password layout with ScrollView (Fix #346) (#349) 2018-07-28 17:06:56 +02:00
m2049r
92728026c7 greek is el (#365) 2018-07-28 16:55:53 +02:00
m2049r
8fd9598c6c increase Xmx for gradle (#364) 2018-07-27 16:20:00 +02:00
m2049r
6633261ba2 cleaned ru & sv translations (#363)
* removed unused strings
* added new strings
2018-07-27 13:06:03 +02:00
uiharu
dc8c8634cb Update strings.xml (#337)
change one instance of "xmr.to" to "XMR.TO" for consistency
2018-07-27 12:10:55 +02:00
v1docq47
dcf9b6db15 translate to russian language #244 (#278)
* Update strings.xml

* Update about.xml

* Update help.xml
2018-07-27 12:10:13 +02:00
Leza89
654d63c32e Added missing translations, corrected some grammar, tried to make it more newbie friendly and shorter (#332)
(i.e. No german speaker will know TX as "transaction" - "Empfangsadresse" instead of "Öffentliche Adresse" should make it more clear that this is where your money goes to)

strings.xml Translation[German]
2018-07-27 12:06:39 +02:00
noxxi
caf91fccfd Swedish translation #236 (#265)
* Translated to SE

All paragraphs and headers have been translated, with the exception of paragraphs and headers included in a string with a translatable="false" attribute.

* Swedish - Task #236: 1/3 complete

About.xml is fully translated, until further notice

*  Swedish - Task #236: 2/3 complete 

Help.xml is fully translated, until further notice

*  Swedish - Task #236: 3/3 complete 

Strings.xml is fully translated, until further notice

* Swedish - Task #236: 1/3 complete

about.xml is fully translated, until further notice
2018-07-27 12:03:06 +02:00
Ordtrogen
41f1f3dec0 Swedish translation of strings.xml (#261)
* Swedish translation of strings.xml

* removed untranslatable strings

* added some missing strings
2018-07-27 11:57:03 +02:00
m2049r
bb66d1f68d cleaned gr & ro translations (#360)
- removed unused strings
- added new strings
2018-07-27 11:49:21 +02:00
AlexUnderHood
310548b031 Romanian translation - strings.xml (#241) 2018-07-27 10:46:11 +02:00
AlexUnderHood
ad7737475f Romanian translation - help.xml (#240) 2018-07-27 10:45:51 +02:00
AlexUnderHood
b2d07a65b7 Romanian translation - about.xml (#239) 2018-07-27 10:45:35 +02:00
gerasimos7777
5dfcaae5b9 Greek translation (#256)
* Create strings.xml

* Create help.xml

* Update strings.xml

* Update help.xml
2018-07-27 10:44:29 +02:00
el00ruobuob
3c91fc060c Fix "restore from seeds" too long on certain devices. (#345)
+ change "restore from key" for consistency.
2018-07-27 10:43:38 +02:00
uiharu
f8f113faab Update zh-rCN "strings.xml" (#338)
- basically mirrors changes for zh-rTW
- fill in new strings
- correct and edit
2018-07-27 10:36:38 +02:00
m2049r
268a00cb3e new version code 2018-07-26 08:42:48 +02:00
m2049r
878500ae71 hamburger on sync only in wallet screen (#357) 2018-07-26 08:42:26 +02:00
m2049r
a0debb0f7e Upgrade Monero Core to v0.12.3.0 (#356)
* upgrade v0.12.3.0 monero api

* new version code
2018-07-24 18:20:27 +02:00
m2049r
3a71e8d352 fix path of build script 2018-06-22 22:43:31 +02:00
m2049r
af68c5e51f new version 1.5.10 2018-06-22 22:11:49 +02:00
m2049r
5a2b48a087 fix displayed subaddress # 2018-06-22 22:09:44 +02:00
m2049r
0776b7b6a3 avoid NPE (#330) 2018-06-21 22:15:14 +02:00
m2049r
c2eed85a83 v1.5.9 2018-06-21 21:51:39 +02:00
m2049r
d7c2b4a727 restore deleted it/string 2018-06-21 20:22:54 +02:00
erciccione
085e41f5da update italian strings (#323) 2018-06-21 20:20:43 +02:00
Re-Diculous
6c6b3061a8 Update strings.xml (#325) 2018-06-21 20:19:32 +02:00
m2049r
5fc15779b7 Update Translations (#329)
* Portuguese

* update italian

* update german
2018-06-21 20:14:30 +02:00
m2049r
520d151f3c new subaddress bump effect (#327) 2018-06-20 18:33:28 +02:00
m2049r
7aad941dab new version v1.5.8 2018-06-18 09:43:11 +02:00
m2049r
01e7693425 subaddresses (#322) 2018-06-18 09:43:32 +02:00
92 changed files with 7731 additions and 732 deletions

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 97
versionName "1.5.7 'Maximum Nacho'"
versionCode 113
versionName "1.6.3 'Nano S'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -45,8 +45,17 @@ android {
// Map for the version code that gives each ABI a value.
def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
// Enumerate translated locales
def availableLocales = ["en"]
new File("app/src/main/res/").eachFileMatch(~/^values-.*/) { file ->
def languageTag = file.name.substring(7).replace("-r", "-")
availableLocales.add(languageTag)
}
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Update string resource: available_locales
variant.resValue("string", "available_locales", availableLocales.join(","))
// Assigns a different version code for each output APK.
variant.outputs.all {
output ->

View File

@@ -8,13 +8,14 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:name=".XmrWalletApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".XmrWalletApplication"
android:theme="@style/MyMaterialTheme">
<activity
@@ -28,19 +29,25 @@
android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity>
<service
android:name=".service.WalletService"
android:description="@string/service_description"
android:exported="false"
android:label="Monero Wallet Service" />
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2018 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef XMRWALLET_LEDGER_H
#define XMRWALLET_LEDGER_H
#ifdef __cplusplus
extern "C"
{
#endif
#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */
typedef long LONG;
typedef unsigned long DWORD;
typedef DWORD *LPDWORD;
typedef unsigned char BYTE;
typedef BYTE *LPBYTE;
typedef const BYTE *LPCBYTE;
typedef char CHAR;
typedef CHAR *LPSTR;
int LedgerFind(char *buffer, size_t len);
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);
#ifdef __cplusplus
}
#endif
#endif //XMRWALLET_LEDGER_H

View File

@@ -0,0 +1,53 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip;
public class BTChipException extends Exception {
private static final long serialVersionUID = 5512803003827126405L;
public BTChipException(String reason) {
super(reason);
}
public BTChipException(String reason, Throwable cause) {
super(reason, cause);
}
public BTChipException(String reason, int sw) {
super(reason);
this.sw = sw;
}
public int getSW() {
return sw;
}
public String toString() {
if (sw == 0) {
return "BTChip Exception : " + getMessage();
} else {
return "BTChip Exception : " + getMessage() + " " + Integer.toHexString(sw);
}
}
private int sw;
}

View File

@@ -0,0 +1,31 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
* (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip.comm;
import com.btchip.BTChipException;
public interface BTChipTransport {
public byte[] exchange(byte[] command);
public void close();
public void setDebug(boolean debugFlag);
}

View File

@@ -0,0 +1,126 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
* (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip.comm;
import java.io.ByteArrayOutputStream;
public class LedgerHelper {
private static final int TAG_APDU = 0x05;
public static byte[] wrapCommandAPDU(int channel, byte[] command, int packetSize) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
if (packetSize < 3) {
throw new IllegalArgumentException("Can't handle Ledger framing with less than 3 bytes for the report");
}
int sequenceIdx = 0;
int offset = 0;
output.write(channel >> 8);
output.write(channel);
output.write(TAG_APDU);
output.write(sequenceIdx >> 8);
output.write(sequenceIdx);
sequenceIdx++;
output.write(command.length >> 8);
output.write(command.length);
int blockSize = (command.length > packetSize - 7 ? packetSize - 7 : command.length);
output.write(command, offset, blockSize);
offset += blockSize;
while (offset != command.length) {
output.write(channel >> 8);
output.write(channel);
output.write(TAG_APDU);
output.write(sequenceIdx >> 8);
output.write(sequenceIdx);
sequenceIdx++;
blockSize = (command.length - offset > packetSize - 5 ? packetSize - 5 : command.length - offset);
output.write(command, offset, blockSize);
offset += blockSize;
}
if ((output.size() % packetSize) != 0) {
byte[] padding = new byte[packetSize - (output.size() % packetSize)];
output.write(padding, 0, padding.length);
}
return output.toByteArray();
}
public static byte[] unwrapResponseAPDU(int channel, byte[] data, int packetSize) {
ByteArrayOutputStream response = new ByteArrayOutputStream();
int offset = 0;
int responseLength;
int sequenceIdx = 0;
if ((data == null) || (data.length < 7 + 5)) {
return null;
}
if (data[offset++] != (channel >> 8)) {
throw new IllegalArgumentException("Invalid channel");
}
if (data[offset++] != (channel & 0xff)) {
throw new IllegalArgumentException("Invalid channel");
}
if (data[offset++] != TAG_APDU) {
throw new IllegalArgumentException("Invalid tag");
}
if (data[offset++] != 0x00) {
throw new IllegalArgumentException("Invalid sequence");
}
if (data[offset++] != 0x00) {
throw new IllegalArgumentException("Invalid sequence");
}
responseLength = ((data[offset++] & 0xff) << 8);
responseLength |= (data[offset++] & 0xff);
if (data.length < 7 + responseLength) {
return null;
}
int blockSize = (responseLength > packetSize - 7 ? packetSize - 7 : responseLength);
response.write(data, offset, blockSize);
offset += blockSize;
while (response.size() != responseLength) {
sequenceIdx++;
if (offset == data.length) {
return null;
}
if (data[offset++] != (channel >> 8)) {
throw new IllegalArgumentException("Invalid channel");
}
if (data[offset++] != (channel & 0xff)) {
throw new IllegalArgumentException("Invalid channel");
}
if (data[offset++] != TAG_APDU) {
throw new IllegalArgumentException("Invalid tag");
}
if (data[offset++] != (sequenceIdx >> 8)) {
throw new IllegalArgumentException("Invalid sequence");
}
if (data[offset++] != (sequenceIdx & 0xff)) {
throw new IllegalArgumentException("Invalid sequence");
}
blockSize = (responseLength - response.size() > packetSize - 5 ? packetSize - 5 : responseLength - response.size());
if (blockSize > data.length - offset) {
return null;
}
response.write(data, offset, blockSize);
offset += blockSize;
}
return response.toByteArray();
}
}

View File

@@ -0,0 +1,145 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
* (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip.comm.android;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import com.btchip.BTChipException;
import com.btchip.comm.BTChipTransport;
import com.btchip.comm.LedgerHelper;
import com.btchip.utils.Dump;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import timber.log.Timber;
public class BTChipTransportAndroidHID implements BTChipTransport {
public static UsbDevice getDevice(UsbManager manager) {
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
for (UsbDevice device : deviceList.values()) {
Timber.d("%04X:%04X %s, %s", device.getVendorId(), device.getProductId(), device.getManufacturerName(), device.getProductName());
if ((device.getVendorId() == VID) && (device.getProductId() == PID_HID)) {
return device;
}
}
return null;
}
public static BTChipTransport open(UsbManager manager, UsbDevice device) throws IOException {
UsbDeviceConnection connection = manager.openDevice(device);
if (connection == null) throw new IOException("Device not connected");
// Must only be called once permission is granted (see http://developer.android.com/reference/android/hardware/usb/UsbManager.html)
// Important if enumerating, rather than being awaken by the intent notification
UsbInterface dongleInterface = device.getInterface(0);
UsbEndpoint in = null;
UsbEndpoint out = null;
for (int i = 0; i < dongleInterface.getEndpointCount(); i++) {
UsbEndpoint tmpEndpoint = dongleInterface.getEndpoint(i);
if (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
in = tmpEndpoint;
} else {
out = tmpEndpoint;
}
}
connection.claimInterface(dongleInterface, true);
return new BTChipTransportAndroidHID(connection, dongleInterface, in, out);
}
private static final int VID = 0x2C97;
private static final int PID_HID = 0x0001;
private UsbDeviceConnection connection;
private UsbInterface dongleInterface;
private UsbEndpoint in;
private UsbEndpoint out;
private byte transferBuffer[];
private boolean debug;
public BTChipTransportAndroidHID(UsbDeviceConnection connection, UsbInterface dongleInterface, UsbEndpoint in, UsbEndpoint out) {
this.connection = connection;
this.dongleInterface = dongleInterface;
this.in = in;
this.out = out;
transferBuffer = new byte[HID_BUFFER_SIZE];
}
@Override
public byte[] exchange(byte[] command) {
ByteArrayOutputStream response = new ByteArrayOutputStream();
byte[] responseData = null;
int offset = 0;
if (debug) {
Timber.d("=> %s", Dump.dump(command));
}
command = LedgerHelper.wrapCommandAPDU(LEDGER_DEFAULT_CHANNEL, command, HID_BUFFER_SIZE);
UsbRequest requestOut = new UsbRequest();
requestOut.initialize(connection, out);
while (offset != command.length) {
int blockSize = (command.length - offset > HID_BUFFER_SIZE ? HID_BUFFER_SIZE : command.length - offset);
System.arraycopy(command, offset, transferBuffer, 0, blockSize);
requestOut.queue(ByteBuffer.wrap(transferBuffer), HID_BUFFER_SIZE);
connection.requestWait();
offset += blockSize;
}
requestOut.close();
ByteBuffer responseBuffer = ByteBuffer.allocate(HID_BUFFER_SIZE);
UsbRequest requestIn = new UsbRequest();
requestIn.initialize(connection, in);
while ((responseData = LedgerHelper.unwrapResponseAPDU(LEDGER_DEFAULT_CHANNEL, response.toByteArray(), HID_BUFFER_SIZE)) == null) {
responseBuffer.clear();
requestIn.queue(responseBuffer, HID_BUFFER_SIZE);
connection.requestWait();
responseBuffer.rewind();
responseBuffer.get(transferBuffer, 0, HID_BUFFER_SIZE);
response.write(transferBuffer, 0, HID_BUFFER_SIZE);
}
requestIn.close();
if (debug) {
Timber.d("<= %s", Dump.dump(responseData));
}
return responseData;
}
@Override
public void close() {
connection.releaseInterface(dongleInterface);
connection.close();
}
@Override
public void setDebug(boolean debugFlag) {
this.debug = debugFlag;
}
private static final int HID_BUFFER_SIZE = 64;
private static final int LEDGER_DEFAULT_CHANNEL = 1;
private static final int SW1_DATA_AVAILABLE = 0x61;
}

View File

@@ -0,0 +1,62 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip.utils;
import java.io.ByteArrayOutputStream;
public class Dump {
public static String dump(byte[] buffer, int offset, int length) {
String result = "";
for (int i = 0; i < length; i++) {
String temp = Integer.toHexString((buffer[offset + i]) & 0xff);
if (temp.length() < 2) {
temp = "0" + temp;
}
result += temp;
}
return result;
}
public static String dump(byte[] buffer) {
return dump(buffer, 0, buffer.length);
}
public static byte[] hexToBin(String src) {
ByteArrayOutputStream result = new ByteArrayOutputStream();
int i = 0;
while (i < src.length()) {
char x = src.charAt(i);
if (!((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'))) {
i++;
continue;
}
try {
result.write(Integer.valueOf("" + src.charAt(i) + src.charAt(i + 1), 16));
i += 2;
} catch (Exception e) {
return null;
}
}
return result.toByteArray();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,7 @@ public class GenerateFragment extends Fragment {
static final String TYPE_NEW = "new";
static final String TYPE_KEY = "key";
static final String TYPE_SEED = "seed";
static final String TYPE_LEDGER = "ledger";
static final String TYPE_VIEWONLY = "view";
private TextInputLayout etWalletName;
@@ -190,6 +191,17 @@ public class GenerateFragment extends Fragment {
return false;
}
});
} else if (type.equals(TYPE_LEDGER)) {
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
etWalletRestoreHeight.requestFocus();
return true;
}
return false;
}
});
} else if (type.equals(TYPE_SEED)) {
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@@ -391,16 +403,24 @@ public class GenerateFragment extends Fragment {
// is it a date?
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setLenient(false);
parser.parse(restoreHeight);
height = RestoreHeight.getInstance().getHeight(restoreHeight);
} catch (ParseException exPE) {
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ex) {
}
if (height <= 0)
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ex) {
}
if (height <= 0)
try {
// or is it a height?
height = Long.parseLong(restoreHeight);
} catch (NumberFormatException exNFE) {
} catch (NumberFormatException ex) {
return -1;
}
}
Timber.d("Using Restore Height = %d", height);
return height;
}
@@ -477,6 +497,12 @@ public class GenerateFragment extends Fragment {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass, seed, height);
} else if (type.equals(TYPE_LEDGER)) {
bGenerate.setEnabled(false);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerateLedger(name, crazyPass, height);
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
if (checkAddress() && checkViewKey() && checkSpendKey()) {
bGenerate.setEnabled(false);
@@ -515,6 +541,8 @@ public class GenerateFragment extends Fragment {
return getString(R.string.generate_wallet_type_new);
case TYPE_SEED:
return getString(R.string.generate_wallet_type_seed);
case TYPE_LEDGER:
return getString(R.string.generate_wallet_type_ledger);
case TYPE_VIEWONLY:
return getString(R.string.generate_wallet_type_view);
default:
@@ -532,6 +560,8 @@ public class GenerateFragment extends Fragment {
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
void onGenerateLedger(String name, String password, long height);
void setTitle(String title);
void setToolbarButton(int type);
@@ -567,6 +597,9 @@ public class GenerateFragment extends Fragment {
case TYPE_SEED:
inflater.inflate(R.menu.create_wallet_seed, menu);
break;
case TYPE_LEDGER:
inflater.inflate(R.menu.create_wallet_ledger, menu);
break;
case TYPE_VIEWONLY:
inflater.inflate(R.menu.create_wallet_view, menu);
break;

View File

@@ -43,6 +43,8 @@ import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -52,8 +54,6 @@ import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
import timber.log.Timber;
public class GenerateReviewFragment extends Fragment {
@@ -76,6 +76,9 @@ public class GenerateReviewFragment extends Fragment {
private ImageButton bCopyAddress;
private LinearLayout llAdvancedInfo;
private LinearLayout llPassword;
private LinearLayout llMnemonic;
private LinearLayout llSpendKey;
private LinearLayout llViewKey;
private Button bAdvancedInfo;
private Button bAccept;
@@ -99,6 +102,9 @@ public class GenerateReviewFragment extends Fragment {
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);
@@ -142,7 +148,6 @@ public class GenerateReviewFragment extends Fragment {
}
void showDetails() {
showProgress();
tvWalletPassword.setText(null);
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, walletPath);
}
@@ -188,6 +193,20 @@ public class GenerateReviewFragment extends Fragment {
boolean isWatchOnly;
Wallet.Status status;
boolean dialogOpened = false;
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgress();
if ((walletPath != null)
&& (WalletManager.getInstance().queryWalletHardware(walletPath + ".keys", getPassword()) == 1)
&& (progressCallback != null)) {
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
dialogOpened = true;
}
}
@Override
protected Boolean doInBackground(String... params) {
if (params.length != 1) return false;
@@ -212,7 +231,11 @@ public class GenerateReviewFragment extends Fragment {
address = wallet.getAddress();
seed = wallet.getSeed();
viewKey = wallet.getSecretViewKey();
if (wallet.isKeyOnDevice()) {
viewKey = Ledger.Key();
} else {
viewKey = wallet.getSecretViewKey();
}
spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey();
isWatchOnly = wallet.isWatchOnly();
if (closeWallet) wallet.close();
@@ -222,6 +245,8 @@ public class GenerateReviewFragment extends Fragment {
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (dialogOpened)
progressCallback.dismissProgressDialog();
if (!isAdded()) return; // never mind
walletName = name;
if (result) {
@@ -232,10 +257,22 @@ public class GenerateReviewFragment extends Fragment {
llPassword.setVisibility(View.VISIBLE);
tvWalletPassword.setText(getPassword());
tvWalletAddress.setText(address);
tvWalletMnemonic.setText(seed);
tvWalletViewKey.setText(viewKey);
tvWalletSpendKey.setText(spendKey);
bAdvancedInfo.setVisibility(View.VISIBLE);
if (!seed.isEmpty()) {
llMnemonic.setVisibility(View.VISIBLE);
tvWalletMnemonic.setText(seed);
}
boolean showAdvanced = false;
if (isKeyValid(viewKey)) {
llViewKey.setVisibility(View.VISIBLE);
tvWalletViewKey.setText(viewKey);
showAdvanced = true;
}
if (isKeyValid(spendKey)) {
llSpendKey.setVisibility(View.VISIBLE);
tvWalletSpendKey.setText(spendKey);
showAdvanced = true;
}
if (showAdvanced) bAdvancedInfo.setVisibility(View.VISIBLE);
bCopyAddress.setClickable(true);
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
activityCallback.setTitle(name, getString(R.string.details_title));
@@ -267,6 +304,8 @@ public class GenerateReviewFragment extends Fragment {
public interface ProgressListener {
void showProgressDialog(int msgId);
void showLedgerProgressDialog(int mode);
void dismissProgressDialog();
}
@@ -577,4 +616,10 @@ public class GenerateReviewFragment extends Fragment {
return openDialog;
}
private boolean isKeyValid(String key) {
return (key != null) && (key.length() == 64)
&& !key.equals("0000000000000000000000000000000000000000000000000000000000000000")
&& !key.toLowerCase().equals("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// ledger implmenetation returns the spend key as f's
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -103,6 +103,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
void setNetworkType(NetworkType networkType);
boolean hasLedger();
}
@Override
@@ -145,11 +146,13 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
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);
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
@@ -163,6 +166,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabView.setOnClickListener(this);
fabKey.setOnClickListener(this);
fabSeed.setOnClickListener(this);
fabLedger.setOnClickListener(this);
fabScreen.setOnClickListener(this);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
@@ -173,7 +177,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
etDummy = (EditText) view.findViewById(R.id.etDummy);
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
Notice.showAll(llNotice,".*_login");
Notice.showAll(llNotice, ".*_login");
etDaemonAddress = (DropDownEditText) view.findViewById(R.id.etDaemonAddress);
nodeAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line);
@@ -426,9 +430,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
private boolean isFabOpen = false;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger;
private FrameLayout fabScreen;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabLedgerL;
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
private Animation fab_pulse;
@@ -437,32 +441,53 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
public void animateFAB() {
if (isFabOpen) {
fabScreen.setVisibility(View.INVISIBLE);
if (isFabOpen) { // close the fab
fabScreen.setClickable(false);
fabScreen.startAnimation(fab_close_screen);
fab.startAnimation(rotate_backward);
fabNewL.startAnimation(fab_close);
fabNew.setClickable(false);
fabViewL.startAnimation(fab_close);
fabView.setClickable(false);
fabKeyL.startAnimation(fab_close);
fabKey.setClickable(false);
fabSeedL.startAnimation(fab_close);
fabSeed.setClickable(false);
if (fabLedgerL.getVisibility() == View.VISIBLE) {
fabLedgerL.startAnimation(fab_close);
fabLedger.setClickable(false);
} else {
fabNewL.startAnimation(fab_close);
fabNew.setClickable(false);
fabViewL.startAnimation(fab_close);
fabView.setClickable(false);
fabKeyL.startAnimation(fab_close);
fabKey.setClickable(false);
fabSeedL.startAnimation(fab_close);
fabSeed.setClickable(false);
}
isFabOpen = false;
} else {
} else { // open the fab
fabScreen.setClickable(true);
fabScreen.startAnimation(fab_open_screen);
fab.startAnimation(rotate_forward);
fabNewL.startAnimation(fab_open);
fabNew.setClickable(true);
fabViewL.startAnimation(fab_open);
fabView.setClickable(true);
fabKeyL.startAnimation(fab_open);
fabKey.setClickable(true);
fabSeedL.startAnimation(fab_open);
fabSeed.setClickable(true);
if (activityCallback.hasLedger()) {
fabLedgerL.setVisibility(View.VISIBLE);
fabNewL.setVisibility(View.GONE);
fabViewL.setVisibility(View.GONE);
fabKeyL.setVisibility(View.GONE);
fabSeedL.setVisibility(View.GONE);
fabLedgerL.startAnimation(fab_open);
fabLedger.setClickable(true);
} else {
fabLedgerL.setVisibility(View.GONE);
fabNewL.setVisibility(View.VISIBLE);
fabViewL.setVisibility(View.VISIBLE);
fabKeyL.setVisibility(View.VISIBLE);
fabSeedL.setVisibility(View.VISIBLE);
fabNewL.startAnimation(fab_open);
fabNew.setClickable(true);
fabViewL.startAnimation(fab_open);
fabView.setClickable(true);
fabKeyL.startAnimation(fab_open);
fabKey.setClickable(true);
fabSeedL.startAnimation(fab_open);
fabSeed.setClickable(true);
}
isFabOpen = true;
}
}
@@ -470,6 +495,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override
public void onClick(View v) {
int id = v.getId();
Timber.d("onClick %d/%d", id, R.id.fabLedger);
switch (id) {
case R.id.fab:
animateFAB();
@@ -491,6 +517,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
break;
case R.id.fabLedger:
Timber.d("FAB_LEDGER");
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
break;
case R.id.fabScreen:
animateFAB();
break;

View File

@@ -0,0 +1,23 @@
/*
* 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;
import com.m2049r.xmrwallet.data.BarcodeData;
public interface OnUriScannedListener {
boolean onUriScanned(BarcodeData barcodeData);
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,13 +16,12 @@
package com.m2049r.xmrwallet;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.m2049r.xmrwallet.util.Helper;
import java.io.File;
import com.m2049r.xmrwallet.util.LocaleHelper;
import static android.view.WindowManager.LayoutParams;
@@ -36,4 +35,9 @@ public abstract class SecureActivity extends AppCompatActivity {
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
}
}
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(LocaleHelper.setLocale(context, LocaleHelper.getLocale(context)));
}
}

View File

@@ -224,7 +224,7 @@ public class TxFragment extends Fragment {
activityCallback.setSubtitle(getString(R.string.tx_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
tvAccount.setText("" + info.subaddrAccount);
tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress));
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
tvTxId.setText(info.hash);
@@ -287,7 +287,10 @@ public class TxFragment extends Fragment {
}
} else {
sb.append("-");
dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ? activityCallback.getWalletAddress() : "-");
dstSb.append(info.direction ==
TransactionInfo.Direction.Direction_In ?
activityCallback.getWalletSubaddress(info.account, info.subaddress) :
"-");
}
tvTxTransfers.setText(sb.toString());
tvDestination.setText(dstSb.toString());
@@ -322,7 +325,7 @@ public class TxFragment extends Fragment {
Listener activityCallback;
public interface Listener {
String getWalletAddress();
String getWalletSubaddress(int accountIndex, int subaddressIndex);
String getTxKey(String hash);

File diff suppressed because it is too large Load Diff

View File

@@ -271,7 +271,7 @@ public class WalletFragment extends Fragment
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
enableAccountsList(true);
if (isVisible()) enableAccountsList(true); //otherwise it is enabled in onResume()
}
boolean walletLoaded = false;

View File

@@ -18,8 +18,10 @@ package com.m2049r.xmrwallet;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.LocaleHelper;
import timber.log.Timber;
@@ -27,9 +29,21 @@ public class XmrWalletApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
}
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(LocaleHelper.setLocale(context, LocaleHelper.getLocale(context)));
}
@Override
public void onConfigurationChanged(Configuration configuration) {
super.onConfigurationChanged(configuration);
LocaleHelper.updateSystemDefaultLocale(configuration.locale);
LocaleHelper.setLocale(XmrWalletApplication.this, LocaleHelper.getLocale(XmrWalletApplication.this));
}
}

View File

@@ -18,9 +18,7 @@ package com.m2049r.xmrwallet.data;
import android.net.Uri;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
import java.util.HashMap;
@@ -45,6 +43,11 @@ public class BarcodeData {
public String paymentId = null;
public String amount = null;
public BarcodeData(String uri) {
this.asset = asset;
this.address = address;
}
public BarcodeData(Asset asset, String address) {
this.asset = asset;
this.address = address;
@@ -63,6 +66,31 @@ public class BarcodeData {
this.amount = amount;
}
public Uri getUri() {
return Uri.parse(getUriString());
}
public String getUriString() {
if (asset != Asset.XMR) throw new IllegalStateException("We can only do XMR stuff!");
StringBuilder sb = new StringBuilder();
sb.append(BarcodeData.XMR_SCHEME).append(address);
boolean first = true;
if ((paymentId != null) && !paymentId.isEmpty()) {
sb.append("?");
first = false;
sb.append(BarcodeData.XMR_PAYMENTID).append('=').append(paymentId);
}
if (!amount.isEmpty()) {
if (first) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(BarcodeData.XMR_AMOUNT).append('=').append(amount);
}
return sb.toString();
}
static public BarcodeData fromQrCode(String qrCode) {
// check for monero uri
BarcodeData bcData = parseMoneroUri(qrCode);

View File

@@ -0,0 +1,130 @@
package com.m2049r.xmrwallet.dialog;
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (C) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
import java.util.Locale;
import timber.log.Timber;
public class ProgressDialog extends AlertDialog {
private ProgressBar pbBar;
private TextView tvMessage;
private TextView tvProgress;
private View rlProgressBar, pbCircle;
static private final String PROGRESS_FORMAT = "%1d/%2d";
private CharSequence message;
private int maxValue, progressValue;
private boolean indeterminate = true;
public ProgressDialog(Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ledger_progress, null);
pbCircle = view.findViewById(R.id.pbCircle);
tvMessage = (TextView) view.findViewById(R.id.tvMessage);
rlProgressBar = view.findViewById(R.id.rlProgressBar);
pbBar = (ProgressBar) view.findViewById(R.id.pbBar);
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
setView(view);
//setTitle("blabla");
//super.setMessage("bubbu");
// view.invalidate();
setIndeterminate(indeterminate);
if (maxValue > 0) {
setMax(maxValue);
}
if (progressValue > 0) {
setProgress(progressValue);
}
if (message != null) {
Timber.d("msg=%s", message);
setMessage(message);
}
super.onCreate(savedInstanceState);
}
public void setProgress(int value, int max) {
progressValue = value;
maxValue = max;
if (pbBar != null) {
pbBar.setProgress(value);
pbBar.setMax(max);
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
}
}
public void setProgress(int value) {
progressValue = value;
if (pbBar != null) {
pbBar.setProgress(value);
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
}
}
public void setMax(int max) {
maxValue = max;
if (pbBar != null) {
pbBar.setMax(max);
}
}
public void setIndeterminate(boolean indeterminate) {
if (this.indeterminate != indeterminate) {
if (rlProgressBar != null) {
if (indeterminate) {
pbCircle.setVisibility(View.VISIBLE);
rlProgressBar.setVisibility(View.GONE);
} else {
pbCircle.setVisibility(View.GONE);
rlProgressBar.setVisibility(View.VISIBLE);
}
}
this.indeterminate = indeterminate;
}
}
@Override
public void setMessage(CharSequence message) {
this.message = message;
if (tvMessage != null) {
tvMessage.setText(message);
}
}
}

View File

@@ -17,6 +17,7 @@
package com.m2049r.xmrwallet.fragment.send;
import android.content.Context;
import android.nfc.NfcManager;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v7.widget.CardView;
@@ -64,6 +65,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public interface Listener {
void setBarcodeData(BarcodeData data);
BarcodeData getBarcodeData();
void setMode(SendFragment.Mode mode);
TxData getTxData();
@@ -83,8 +86,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public interface OnScanListener {
void onScan();
BarcodeData popScannedData();
}
@Override
@@ -206,6 +207,11 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());
View tvNfc = view.findViewById(R.id.tvNfc);
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
if ((manager != null) && (manager.getDefaultAdapter() != null))
tvNfc.setVisibility(View.VISIBLE);
return view;
}
@@ -298,11 +304,20 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public void onResume() {
super.onResume();
Timber.d("onResume");
BarcodeData data = onScanListener.popScannedData();
sendListener.setBarcodeData(data);
if (data != null) {
processScannedData();
}
public void processScannedData(BarcodeData barcodeData) {
sendListener.setBarcodeData(barcodeData);
if (isResumed())
processScannedData();
}
public void processScannedData() {
BarcodeData barcodeData = sendListener.getBarcodeData();
if (barcodeData != null) {
Timber.d("GOT DATA");
String scannedAddress = data.address;
String scannedAddress = barcodeData.address;
if (scannedAddress != null) {
etAddress.getEditText().setText(scannedAddress);
checkAddress();
@@ -310,7 +325,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etAddress.getEditText().getText().clear();
etAddress.setError(null);
}
String scannedPaymenId = data.paymentId;
String scannedPaymenId = barcodeData.paymentId;
if (scannedPaymenId != null) {
etPaymentId.getEditText().setText(scannedPaymenId);
checkPaymentId();
@@ -318,7 +333,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etPaymentId.getEditText().getText().clear();
etPaymentId.setError(null);
}
}
} else
Timber.d("barcodeData=null");
}
@Override

View File

@@ -37,16 +37,15 @@ import android.widget.EditText;
import android.widget.Toast;
import com.m2049r.xmrwallet.OnBackPressedListener;
import com.m2049r.xmrwallet.OnUriScannedListener;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.SpendViewPager;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.NodeList;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.UserNotes;
import com.m2049r.xmrwallet.widget.DotBar;
@@ -62,7 +61,7 @@ public class SendFragment extends Fragment
SendSettingsWizardFragment.Listener,
SendConfirmWizardFragment.Listener,
SendSuccessWizardFragment.Listener,
OnBackPressedListener {
OnBackPressedListener, OnUriScannedListener {
private Listener activityCallback;
@@ -86,6 +85,8 @@ public class SendFragment extends Fragment
void setTitle(String title);
void setSubtitle(String subtitle);
void setOnUriScannedListener(OnUriScannedListener onUriScannedListener);
}
private EditText etDummy;
@@ -99,10 +100,6 @@ public class SendFragment extends Fragment
private Button bDone;
private View llXmrToEnabled;
private View ibXmrToInfoClose;
static private int MAX_FALLBACK = Integer.MAX_VALUE;
@Override
@@ -121,7 +118,7 @@ public class SendFragment extends Fragment
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
Notice.showAll(llNotice,".*_send");
Notice.showAll(llNotice, ".*_send");
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
@@ -226,13 +223,20 @@ public class SendFragment extends Fragment
Timber.d("onAttach %s", context);
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
activityCallback = (Listener) context;
activityCallback.setOnUriScannedListener(this);
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onDetach() {
activityCallback.setOnUriScannedListener(null);
super.onDetach();
}
private SpendViewPager spendViewPager;
private SpendPagerAdapter pagerAdapter;
@@ -247,6 +251,18 @@ public class SendFragment extends Fragment
}
}
@Override
public boolean onUriScanned(BarcodeData barcodeData) {
if (spendViewPager.getCurrentItem() == SpendPagerAdapter.POS_ADDRESS) {
final SendWizardFragment fragment = pagerAdapter.getFragment(SpendPagerAdapter.POS_ADDRESS);
if (fragment instanceof SendAddressWizardFragment) {
((SendAddressWizardFragment) fragment).processScannedData(barcodeData);
return true;
}
}
return false;
}
enum Mode {
XMR, BTC
}
@@ -414,8 +430,14 @@ public class SendFragment extends Fragment
barcodeData = data;
}
@Override
public BarcodeData getBarcodeData() {
return barcodeData;
}
@Override
public BarcodeData popBarcodeData() {
Timber.d("POPPED");
BarcodeData data = barcodeData;
barcodeData = null;
return data;

View File

@@ -166,7 +166,11 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
}
if ((userNotes.note.isEmpty())) {
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId);
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ?
(infoItem.subaddress != 0 ?
(context.getString(R.string.tx_subaddress, infoItem.subaddress)) :
"") :
infoItem.paymentId);
} else {
this.tvPaymentId.setText(userNotes.note);
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.ledger;
public enum Instruction {
INS_NONE(0x00),
INS_RESET(0x02),
INS_GET_KEY(0x20),
INS_PUT_KEY(0x22),
INS_GET_CHACHA8_PREKEY(0x24),
INS_VERIFY_KEY(0x26),
INS_SECRET_KEY_TO_PUBLIC_KEY(0x30),
INS_GEN_KEY_DERIVATION(0x32),
INS_DERIVATION_TO_SCALAR(0x34),
INS_DERIVE_PUBLIC_KEY(0x36),
INS_DERIVE_SECRET_KEY(0x38),
INS_GEN_KEY_IMAGE(0x3A),
INS_SECRET_KEY_ADD(0x3C),
INS_SECRET_KEY_SUB(0x3E),
INS_GENERATE_KEYPAIR(0x40),
INS_SECRET_SCAL_MUL_KEY(0x42),
INS_SECRET_SCAL_MUL_BASE(0x44),
INS_DERIVE_SUBADDRESS_PUBLIC_KEY(0x46),
INS_GET_SUBADDRESS(0x48),
INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY(0x4A),
INS_GET_SUBADDRESS_SECRET_KEY(0x4C),
INS_OPEN_TX(0x70),
INS_SET_SIGNATURE_MODE(0x72),
INS_GET_ADDITIONAL_KEY(0x74),
INS_STEALTH(0x76),
INS_BLIND(0x78),
INS_UNBLIND(0x7A),
INS_VALIDATE(0x7C),
INS_MLSAG(0x7E),
INS_CLOSE_TX(0x80),
INS_GET_RESPONSE(0xc0),
INS_UNDEFINED(0xff);;
public static Instruction fromByte(byte n) {
switch (n & 0xFF) {
case 0x00:
return INS_NONE;
case 0x02:
return INS_RESET;
case 0x20:
return INS_GET_KEY;
case 0x22:
return INS_PUT_KEY;
case 0x24:
return INS_GET_CHACHA8_PREKEY;
case 0x26:
return INS_VERIFY_KEY;
case 0x30:
return INS_SECRET_KEY_TO_PUBLIC_KEY;
case 0x32:
return INS_GEN_KEY_DERIVATION;
case 0x34:
return INS_DERIVATION_TO_SCALAR;
case 0x36:
return INS_DERIVE_PUBLIC_KEY;
case 0x38:
return INS_DERIVE_SECRET_KEY;
case 0x3A:
return INS_GEN_KEY_IMAGE;
case 0x3C:
return INS_SECRET_KEY_ADD;
case 0x3E:
return INS_SECRET_KEY_SUB;
case 0x40:
return INS_GENERATE_KEYPAIR;
case 0x42:
return INS_SECRET_SCAL_MUL_KEY;
case 0x44:
return INS_SECRET_SCAL_MUL_BASE;
case 0x46:
return INS_DERIVE_SUBADDRESS_PUBLIC_KEY;
case 0x48:
return INS_GET_SUBADDRESS;
case 0x4A:
return INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY;
case 0x4C:
return INS_GET_SUBADDRESS_SECRET_KEY;
case 0x70:
return INS_OPEN_TX;
case 0x72:
return INS_SET_SIGNATURE_MODE;
case 0x74:
return INS_GET_ADDITIONAL_KEY;
case 0x76:
return INS_STEALTH;
case 0x78:
return INS_BLIND;
case 0x7A:
return INS_UNBLIND;
case 0x7C:
return INS_VALIDATE;
case 0x7E:
return INS_MLSAG;
case 0x80:
return INS_CLOSE_TX;
case 0xc0:
return INS_GET_RESPONSE;
default:
return INS_UNDEFINED;
}
}
public int getValue() {
return value;
}
private int value;
Instruction(int value) {
this.value = value;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
package com.m2049r.xmrwallet.ledger;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.dialog.ProgressDialog;
import com.m2049r.xmrwallet.model.WalletManager;
import timber.log.Timber;
public class LedgerProgressDialog extends ProgressDialog implements Ledger.Listener {
static public final int TYPE_DEBUG = 0;
static public final int TYPE_RESTORE = 1;
static public final int TYPE_SUBADDRESS = 2;
static public final int TYPE_ACCOUNT = 3;
static public final int TYPE_SEND = 4;
private final int type;
private Handler uiHandler = new Handler(Looper.getMainLooper());
public LedgerProgressDialog(Context context, int type) {
super(context);
this.type = type;
setCancelable(false);
if (type == TYPE_SEND)
setMessage(context.getString(R.string.info_prepare_tx));
else
setMessage(context.getString(R.string.progress_ledger_progress));
}
@Override
public void onBackPressed() {
// prevent back button
}
private int firstSubaddress = Integer.MAX_VALUE;
private boolean validate = false;
private boolean validated = false;
@Override
public void onInstructionSend(final Instruction ins, final byte[] apdu) {
Timber.d("LedgerProgressDialog SEND %s", ins);
uiHandler.post(new Runnable() {
@Override
public void run() {
if (type > TYPE_DEBUG) {
validate = false;
switch (ins) {
case INS_RESET: // ledger may ask for confirmation - maybe a bug?
case INS_GET_KEY: // ledger asks for confirmation to send keys
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
break;
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
//00 4a 00 00 09 00 01000000 30000000
// 0 1 2 3 4 5 6 7 8 9 a b c d
int account = bytesToInteger(apdu, 6);
int subaddress = bytesToInteger(apdu, 10);
Timber.d("fetching subaddress (%d, %d)", account, subaddress);
switch (type) {
case TYPE_RESTORE:
setProgress(account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress + 1,
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
case TYPE_ACCOUNT:
final int requestedSubaddress = account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress;
if (firstSubaddress > requestedSubaddress) {
firstSubaddress = requestedSubaddress;
}
setProgress(requestedSubaddress - firstSubaddress + 1,
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
case TYPE_SUBADDRESS:
if (firstSubaddress > subaddress) {
firstSubaddress = subaddress;
}
setProgress(subaddress - firstSubaddress + 1, Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
default:
setIndeterminate(true);
break;
}
setMessage(getContext().getString(R.string.progress_ledger_lookahead));
break;
case INS_VERIFY_KEY:
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_verify));
break;
case INS_OPEN_TX:
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_opentx));
break;
case INS_MLSAG:
if (validated) {
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_mlsag));
}
break;
case INS_VALIDATE:
if ((apdu[2] != 1) || (apdu[3] != 1)) break;
validate = true;
uiHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (validate) {
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
validated = true;
}
}
}, 250);
break;
default:
// ignore others and maintain state
}
} else {
setMessage(ins.name());
}
}
});
}
@Override
public void onInstructionReceive(final Instruction ins, final byte[] data) {
Timber.d("LedgerProgressDialog RECV %s", ins);
uiHandler.post(new Runnable() {
@Override
public void run() {
if (type > TYPE_DEBUG) {
switch (ins) {
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
case INS_VERIFY_KEY:
case INS_GET_CHACHA8_PREKEY:
break;
default:
if (type != TYPE_SEND)
setMessage(getContext().getString(R.string.progress_ledger_progress));
}
} else {
setMessage("Returned from " + ins.name());
}
}
});
}
// TODO: we use ints in Java but the are signed; accounts & subaddresses are unsigned ...
private int bytesToInteger(byte[] bytes, int offset) {
int result = 0;
for (int i = 3; i >= 0; i--) {
result <<= 8;
result |= (bytes[offset + i] & 0xFF);
}
return result;
}
}

View File

@@ -75,7 +75,7 @@ public class TransactionHistory {
Timber.d("refreshed %d", t.size());
for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) {
TransactionInfo info = iterator.next();
if (info.subaddrAccount != accountIndex) {
if (info.account != accountIndex) {
iterator.remove();
Timber.d("removed %s", info.hash);
} else {

View File

@@ -67,7 +67,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
public String hash;
public long timestamp;
public String paymentId;
public int subaddrAccount;
public int account;
public int subaddress;
public long confirmations;
public List<Transfer> transfers;
@@ -84,7 +85,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
String hash,
long timestamp,
String paymentId,
int subaddrAccount,
int account,
int subaddress,
long confirmations,
List<Transfer> transfers) {
this.direction = Direction.values()[direction];
@@ -96,7 +98,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
this.hash = hash;
this.timestamp = timestamp;
this.paymentId = paymentId;
this.subaddrAccount = subaddrAccount;
this.account = account;
this.subaddress = subaddress;
this.confirmations = confirmations;
this.transfers = transfers;
}
@@ -116,7 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(hash);
out.writeLong(timestamp);
out.writeString(paymentId);
out.writeInt(subaddrAccount);
out.writeInt(account);
out.writeInt(subaddress);
out.writeLong(confirmations);
out.writeList(transfers);
out.writeString(txKey);
@@ -143,7 +147,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
hash = in.readString();
timestamp = in.readLong();
paymentId = in.readString();
subaddrAccount = in.readInt();
account = in.readInt();
subaddress = in.readInt();
confirmations = in.readLong();
transfers = in.readArrayList(Transfer.class.getClassLoader());
txKey = in.readString();

View File

@@ -19,6 +19,9 @@ package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.TxData;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import timber.log.Timber;
@@ -86,10 +89,22 @@ public class Wallet {
public native boolean setPassword(String password);
public String getAddress() {
return getAddressJ(accountIndex);
return getAddress(accountIndex);
}
private native String getAddressJ(int accountIndex);
public String getAddress(int accountIndex) {
return getAddressJ(accountIndex, 0);
}
public String getSubaddress(int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
public String getSubaddress(int accountIndex, int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
private native String getAddressJ(int accountIndex, int addressIndex);
public native String getPath();
@@ -109,7 +124,9 @@ public class Wallet {
public native String getSecretSpendKey();
public boolean store() {
return store("");
final boolean ok = store("");
Timber.d("stored");
return ok;
}
public native boolean store(String path);
@@ -133,7 +150,10 @@ public class Wallet {
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
public native long getRestoreHeight();
// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
// virtual bool connectToDaemon() = 0;
public ConnectionStatus getConnectionStatus() {
@@ -323,17 +343,21 @@ public class Wallet {
public String getAccountLabel(int accountIndex) {
String label = getSubaddressLabel(accountIndex, 0);
if (label.equals(NEW_ACCOUNT_NAME)) {
String address = getAddressJ(accountIndex);
String address = getAddress(accountIndex);
int len = address.length();
return address.substring(0, 6) +
"\u2026" + address.substring(len - 6, len);
} else return label;
}
public String getSubaddressLabel(int addressIndex) {
return getSubaddressLabel(accountIndex, addressIndex);
}
public native String getSubaddressLabel(int accountIndex, int addressIndex);
public void setAccountLabel(String label) {
setSubaddressLabel(accountIndex, 0, label);
setAccountLabel(accountIndex, label);
}
public void setAccountLabel(int accountIndex, String label) {
@@ -342,9 +366,31 @@ public class Wallet {
public native void setSubaddressLabel(int accountIndex, int addressIndex, String label);
public int numAccounts() {
return numSubaddressAccounts();
public native int getNumAccounts();
public int getNumSubaddresses() {
return getNumSubaddresses(accountIndex);
}
public native int numSubaddressAccounts();
public native int getNumSubaddresses(int accountIndex);
public String getNewSubaddress() {
return getNewSubaddress(accountIndex);
}
public String getNewSubaddress(int accountIndex) {
String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).format(new Date());
addSubaddress(accountIndex, timeStamp);
String subaddress = getLastSubaddress(accountIndex);
Timber.d("%d: %s", getNumSubaddresses(accountIndex) - 1, subaddress);
return subaddress;
}
public native void addSubaddress(int accountIndex, String label);
public String getLastSubaddress(int accountIndex) {
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
}
public native boolean isKeyOnDevice();
}

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