1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-04 17:28:42 +02:00

Compare commits

..

74 Commits

Author SHA1 Message Date
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
m2049r
091538752b add our stagenet server to defaults (#321) 2018-06-17 13:53:01 +02:00
m2049r
d5b95dd976 new version v1.5.7 2018-06-17 13:31:33 +02:00
m2049r
008f06959c remove mixin settings (#320) 2018-06-17 13:22:29 +02:00
m2049r
817816cd34 Layout tweaks (#319)
* sweep layout tweak

* reduce label to fit

* change sweep message
2018-06-17 13:21:59 +02:00
m2049r
9d41d5da52 new version v1.5.6 2018-06-16 15:27:28 +02:00
m2049r
ad76a7ffc1 Fixes to v1.5.5 (#318)
* fix disappearing hamburger

* german fixes to fit space
2018-06-16 15:20:13 +02:00
el00ruobuob
475542c4f3 French Strings updated to #297 (#316)
+ Missing update to #312
+ enhancement on "wrapping things up"
2018-06-16 15:19:40 +02:00
m2049r
b5d0659ca9 sweep all (#317) 2018-06-16 15:17:48 +02:00
m2049r
781bfbc78b Update FAQ.md 2018-06-14 23:40:41 +02:00
m2049r
8985511209 fix length test (#314) 2018-06-14 22:28:33 +02:00
m2049r
3c8a4ce967 Merge branch 'master' of https://github.com/m2049r/xmrwallet 2018-06-14 21:59:48 +02:00
m2049r
fcfedbcfae Fix balance (#313)
* new version

* fix balance
2018-06-14 22:00:15 +02:00
m2049r
74279b135a new version 2018-06-14 21:37:19 +02:00
0140454
d6d2de8312 Inform user the progress or result of opening wallet (#297) 2018-06-14 21:33:23 +02:00
m2049r
af0ecb2894 accounts (#312) 2018-06-14 21:32:52 +02:00
m2049r
975cc4f43c show correct amount for pending tx (#309) 2018-06-11 10:21:22 +02:00
0140454
74ba36de26 Use FingerprintManager instead of FingerprintManagerCompat (Fix #300) (#302) 2018-06-10 10:59:20 +02:00
m2049r
7627e15a48 Fix keystore null (#308)
* avoid crash if input to large

* avoid NPE if wallet key not found
2018-06-10 10:57:24 +02:00
m2049r
37244cb9e0 coinmarketcap for exchange rates (#304) 2018-06-10 10:56:46 +02:00
m2049r
843566b820 spinner tweaks (#306) 2018-06-09 12:42:09 +02:00
m2049r
0bcf156929 update gradle version (#305) 2018-06-09 10:21:44 +02:00
m2049r
b1d91e2671 new version 2018-05-27 10:59:10 +02:00
m2049r
271cd2d4a8 deal with all broken variants (#292)
* remove variant code for arm32

* deal with all broken variants
2018-05-25 23:44:37 +02:00
m2049r
22c5a543db upgrade to v0.12.1.0 (#291) 2018-05-25 22:38:05 +02:00
m2049r
cd986860c5 remove variant code for arm32 (#290) 2018-05-25 22:37:50 +02:00
m2049r
0cf5981eae Fixes "Invalid Password" although password correct (#289)
* don't log warning

* fix cn_slow_hash variant&prehash
cn_slow_hash signature was changed in monero-core but the linker didn't
notice - also added code to support wallets created with variant &
prehash enabled
2018-05-25 18:20:48 +02:00
m2049r
e109df34f0 remove if save fails (#281) 2018-05-25 18:20:27 +02:00
m2049r
5a7aa6cc77 testnet => stagenet (#288) 2018-05-25 18:19:29 +02:00
m2049r
f50629ff81 check for encoded pw (#280) 2018-05-10 15:26:44 +02:00
m2049r
cb12d64e5f Various Fixes (#279)
* load password only if it's passed

* cancel fingerprint if password entered

* rework fingerprint code

* cleanup unused params

* new version code
2018-05-10 13:48:11 +02:00
m2049r
a8f08fb9b9 new version 2018-05-06 17:46:48 +02:00
m2049r
3e9be418a8 removed removed strings (#276) 2018-05-06 12:30:01 +02:00
KillASIC.com
fa5dc9988d Monero translation to simplified chinese. (#263) 2018-05-06 12:17:09 +02:00
m2049r
857cf8d6d8 Random fixes (#275)
* reduce label length

* api fix

* show correct password after change
2018-05-06 12:12:53 +02:00
128 changed files with 9135 additions and 990 deletions

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 91
versionName "1.5.1 'CrAzY Nacho'"
versionCode 111
versionName "1.6.1 '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

@@ -10,11 +10,11 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:name=".XmrWalletApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".XmrWalletApplication"
android:theme="@style/MyMaterialTheme">
<activity
@@ -28,13 +28,39 @@
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>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity>
<!--activity
android:name=".util.UsbEventReceiverActivity"
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/app_name"
android:noHistory="true"
android:process=":UsbEventReceiverActivityProcess"
android:taskAffinity="com.m2049r.xmrwallet.taskAffinityUsbEventReceiver"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity-->
<service
android:name=".service.WalletService"
android:description="@string/service_description"

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,15 @@ enum {
HASH_DATA_AREA = 136
};
void cn_slow_hash(const void *data, size_t length, char *hash);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
inline void slow_hash(const void *data, const size_t length, char *hash) {
cn_slow_hash(data, length, hash, 0 /* variant */, 0/*prehashed*/);
}
inline void slow_hash_broken(const void *data, char *hash, int variant) {
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/);
}
#ifdef __cplusplus
}

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();
}
}

View File

@@ -0,0 +1,105 @@
package com.m2049r.xmrwallet;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import com.m2049r.xmrwallet.dialog.ProgressDialog;
import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import timber.log.Timber;
public class BaseActivity extends SecureActivity implements GenerateReviewFragment.ProgressListener {
ProgressDialog progressDialog = null;
private class SimpleProgressDialog extends ProgressDialog {
SimpleProgressDialog(Context context, int msgId) {
super(context);
setCancelable(false);
setMessage(context.getString(msgId));
}
@Override
public void onBackPressed() {
// prevent back button
}
}
@Override
public void showProgressDialog(int msgId) {
showProgressDialog(msgId, 0);
}
public void showProgressDialog(int msgId, long delay) {
dismissProgressDialog(); // just in case
progressDialog = new SimpleProgressDialog(BaseActivity.this, msgId);
if (delay > 0) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
if (progressDialog != null) progressDialog.show();
}
}, delay);
} else {
progressDialog.show();
}
}
@Override
public void showLedgerProgressDialog(int mode) {
dismissProgressDialog(); // just in case
progressDialog = new LedgerProgressDialog(BaseActivity.this, mode);
Ledger.setListener((Ledger.Listener) progressDialog);
progressDialog.show();
}
@Override
public void dismissProgressDialog() {
if (progressDialog == null) return; // nothing to do
if (progressDialog instanceof Ledger.Listener) {
Ledger.unsetListener((Ledger.Listener) progressDialog);
}
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = null;
}
static final int RELEASE_WAKE_LOCK_DELAY = 5000; // millisconds
private PowerManager.WakeLock wl = null;
void acquireWakeLock() {
if ((wl != null) && wl.isHeld()) return;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
this.wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, getString(R.string.app_name));
try {
wl.acquire();
Timber.d("WakeLock acquired");
} catch (SecurityException ex) {
Timber.w("WakeLock NOT acquired: %s", ex.getLocalizedMessage());
wl = null;
}
}
void releaseWakeLock(int delayMillis) {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
releaseWakeLock();
}
}, delayMillis);
}
void releaseWakeLock() {
if ((wl == null) || !wl.isHeld()) return;
wl.release();
wl = null;
Timber.d("WakeLock released");
}
}

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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@@ -59,6 +59,7 @@ public class TxFragment extends Fragment {
TS_FORMATTER.setTimeZone(tz);
}
private TextView tvAccount;
private TextView tvTxTimestamp;
private TextView tvTxId;
private TextView tvTxKey;
@@ -88,6 +89,7 @@ public class TxFragment extends Fragment {
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);
@@ -222,6 +224,8 @@ public class TxFragment extends Fragment {
activityCallback.setSubtitle(getString(R.string.tx_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
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);
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
@@ -236,9 +240,6 @@ public class TxFragment extends Fragment {
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
long realAmount = info.amount;
if (info.isPending) {
realAmount = realAmount - info.fee;
}
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
if ((info.fee > 0)) {
@@ -286,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());
@@ -321,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

@@ -43,9 +43,7 @@ import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
import com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
@@ -101,7 +99,9 @@ public class WalletFragment extends Fragment
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
sCurrency.setAdapter(ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance));
ArrayAdapter currencyAdapter = ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance);
currencyAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown_item);
sCurrency.setAdapter(currencyAdapter);
bSend = (Button) view.findViewById(R.id.bSend);
bReceive = (Button) view.findViewById(R.id.bReceive);
@@ -150,7 +150,7 @@ public class WalletFragment extends Fragment
// at this point selection is XMR in case of error
String displayB;
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // crash if this fails!
if (!"XMR".equals(balanceCurrency)) { // not XMR
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
double amountB = amountA * balanceRate;
displayB = Helper.getFormattedAmount(amountB, false);
} else { // XMR
@@ -159,10 +159,10 @@ public class WalletFragment extends Fragment
tvBalance.setText(displayB);
}
String balanceCurrency = "XMR";
String balanceCurrency = Helper.CRYPTO;
double balanceRate = 1.0;
private final ExchangeApi exchangeApi = new ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
void refreshBalance() {
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
@@ -170,9 +170,10 @@ public class WalletFragment extends Fragment
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
} else { // not XMR
String currency = (String) sCurrency.getSelectedItem();
Timber.d(currency);
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
showExchanging();
exchangeApi.queryExchangeRate("XMR", currency,
exchangeApi.queryExchangeRate(Helper.CRYPTO, currency,
new ExchangeCallback() {
@Override
public void onSuccess(final ExchangeRate exchangeRate) {
@@ -228,10 +229,10 @@ public class WalletFragment extends Fragment
public void exchange(final ExchangeRate exchangeRate) {
hideExchanging();
if (!"XMR".equals(exchangeRate.getBaseCurrency())) {
if (!Helper.CRYPTO.equals(exchangeRate.getBaseCurrency())) {
Timber.e("Not XMR");
sCurrency.setSelection(0, true);
balanceCurrency = "XMR";
balanceCurrency = Helper.CRYPTO;
balanceRate = 1.0;
} else {
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
@@ -256,7 +257,7 @@ public class WalletFragment extends Fragment
// called from activity
public void onRefreshed(final Wallet wallet, final boolean full) {
Timber.d("onRefreshed()");
Timber.d("onRefreshed(%b)", full);
if (full) {
List<TransactionInfo> list = wallet.getHistory().getAll();
adapter.setInfos(list);
@@ -270,6 +271,7 @@ public class WalletFragment extends Fragment
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
if (isVisible()) enableAccountsList(true); //otherwise it is enabled in onResume()
}
boolean walletLoaded = false;
@@ -313,7 +315,7 @@ public class WalletFragment extends Fragment
if (wallet == null) return;
walletTitle = wallet.getName();
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
walletSubtitle = wallet.getAddress().substring(0, 10) + "…" + watchOnly;
walletSubtitle = wallet.getAccountLabel();
activityCallback.setTitle(walletTitle, walletSubtitle);
Timber.d("wallet title is %s", walletTitle);
}
@@ -323,10 +325,13 @@ public class WalletFragment extends Fragment
private String walletSubtitle = null;
private long unlockedBalance = 0;
private int accountIdx = -1;
private void updateStatus(Wallet wallet) {
if (!isAdded()) return;
Timber.d("updateStatus()");
if (walletTitle == null) {
if ((walletTitle == null) || (accountIdx != wallet.getAccountIndex())) {
accountIdx = wallet.getAccountIndex();
setActivityTitle(wallet);
}
long balance = wallet.getBalance();
@@ -412,9 +417,28 @@ public class WalletFragment extends Fragment
super.onResume();
Timber.d("onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle);
activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE);
//activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE); // TODO: Close button somewhere else
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
setProgress(syncProgress);
setProgress(syncText);
showReceive();
if (activityCallback.isSynced()) enableAccountsList(true);
}
@Override
public void onPause() {
enableAccountsList(false);
super.onPause();
}
public interface DrawerLocker {
void setDrawerEnabled(boolean enabled);
}
private void enableAccountsList(boolean enable) {
if (activityCallback instanceof DrawerLocker) {
((DrawerLocker) activityCallback).setDrawerEnabled(enable);
}
}
}

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

@@ -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

@@ -233,7 +233,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean isBitcoinAddress() {
String address = etAddress.getEditText().getText().toString();
if ((address.length() >= 27) && (address.length() <= 34))
if ((address.length() >= 27) && (address.length() <= 35))
return BitcoinAddressValidator.validate(address);
else
return false;

View File

@@ -20,6 +20,9 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
@@ -57,8 +60,9 @@ public class SendAmountWizardFragment extends SendWizardFragment {
private TextView tvFunds;
private ExchangeTextView evAmount;
//private Button bSendAll;
private NumberPadView numberPad;
private View llAmount;
private View rlSweep;
private ImageButton ibSweep;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -73,37 +77,64 @@ public class SendAmountWizardFragment extends SendWizardFragment {
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
evAmount = (ExchangeTextView) view.findViewById(R.id.evAmount);
numberPad = (NumberPadView) view.findViewById(R.id.numberPad);
numberPad.setListener(evAmount);
((NumberPadView) view.findViewById(R.id.numberPad)).setListener(evAmount);
/*
bSendAll = (Button) view.findViewById(R.id.bSendAll);
bSendAll.setOnClickListener(new View.OnClickListener() {
rlSweep = view.findViewById(R.id.rlSweep);
llAmount = view.findViewById(R.id.llAmount);
view.findViewById(R.id.ivSweep).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO: send all - figure out how to display this
sweepAll(false);
}
});
ibSweep = (ImageButton) view.findViewById(R.id.ibSweep);
ibSweep.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sweepAll(true);
}
});
*/
Helper.hideKeyboard(getActivity());
return view;
}
private boolean spendAllMode = false;
private void sweepAll(boolean spendAllMode) {
if (spendAllMode) {
ibSweep.setVisibility(View.INVISIBLE);
llAmount.setVisibility(View.GONE);
rlSweep.setVisibility(View.VISIBLE);
} else {
ibSweep.setVisibility(View.VISIBLE);
llAmount.setVisibility(View.VISIBLE);
rlSweep.setVisibility(View.GONE);
}
this.spendAllMode = spendAllMode;
}
@Override
public boolean onValidateFields() {
if (!evAmount.validate(maxFunds)) {
return false;
}
if (spendAllMode) {
if (sendListener != null) {
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
}
} else {
if (!evAmount.validate(maxFunds)) {
return false;
}
if (sendListener != null) {
String xmr = evAmount.getAmount();
if (xmr != null) {
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
} else {
sendListener.getTxData().setAmount(0L);
if (sendListener != null) {
String xmr = evAmount.getAmount();
if (xmr != null) {
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
} else {
sendListener.getTxData().setAmount(0L);
}
}
}
return true;

View File

@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
private XmrToApi xmrToApi = null;
private final XmrToApi getXmrToApi() {
private XmrToApi getXmrToApi() {
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {

View File

@@ -36,6 +36,7 @@ import com.m2049r.xmrwallet.util.UserNotes;
import timber.log.Timber;
public class SendSettingsWizardFragment extends SendWizardFragment {
final static public int MIXIN = 6;
public static SendSettingsWizardFragment newInstance(Listener listener) {
SendSettingsWizardFragment instance = new SendSettingsWizardFragment();
@@ -54,15 +55,12 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
TxData getTxData();
}
// Mixin = Ringsize - 1
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
final static PendingTransaction.Priority Priorities[] =
{PendingTransaction.Priority.Priority_Default,
PendingTransaction.Priority.Priority_Low,
PendingTransaction.Priority.Priority_Medium,
PendingTransaction.Priority.Priority_High}; // must match the layout XML
private Spinner sMixin;
private Spinner sPriority;
private EditText etNotes;
private EditText etDummy;
@@ -77,7 +75,6 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
View view = inflater.inflate(
R.layout.fragment_send_settings, container, false);
sMixin = (Spinner) view.findViewById(R.id.sMixin);
sPriority = (Spinner) view.findViewById(R.id.sPriority);
etNotes = (EditText) view.findViewById(R.id.etNotes);
@@ -104,7 +101,7 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
if (sendListener != null) {
TxData txData = sendListener.getTxData();
txData.setPriority(Priorities[sPriority.getSelectedItemPosition()]);
txData.setMixin(Mixins[sMixin.getSelectedItemPosition()]);
txData.setMixin(MIXIN);
txData.setUserNotes(new UserNotes(etNotes.getText().toString()));
}
return true;

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

@@ -17,8 +17,11 @@
package com.m2049r.xmrwallet.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import timber.log.Timber;
public class TransactionHistory {
static {
System.loadLibrary("monerujo");
@@ -26,8 +29,18 @@ public class TransactionHistory {
private long handle;
public TransactionHistory(long handle) {
int accountIndex;
public void setAccountFor(Wallet wallet) {
if (accountIndex != wallet.getAccountIndex()) {
this.accountIndex = wallet.getAccountIndex();
refreshWithNotes(wallet);
}
}
public TransactionHistory(long handle, int accountIndex) {
this.handle = handle;
this.accountIndex = accountIndex;
}
public void loadNotes(Wallet wallet) {
@@ -36,7 +49,7 @@ public class TransactionHistory {
}
}
public native int getCount();
public native int getCount(); // over all accounts/subaddresses
//private native long getTransactionByIndexJ(int i);
@@ -53,8 +66,23 @@ public class TransactionHistory {
loadNotes(wallet);
}
// public void refresh() {
// transactions = refreshJ();
// }
public void refresh() {
transactions = refreshJ();
List<TransactionInfo> t = refreshJ();
Timber.d("refreshed %d", t.size());
for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) {
TransactionInfo info = iterator.next();
if (info.account != accountIndex) {
iterator.remove();
Timber.d("removed %s", info.hash);
} else {
Timber.d("kept %s", info.hash);
}
}
transactions = t;
}
private native List<TransactionInfo> refreshJ();

View File

@@ -53,6 +53,11 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
}
}
// virtual std::set<uint32_t> subaddrIndex() const = 0;
// virtual uint32_t subaddrAccount() const = 0;
// virtual std::string label() const = 0;
// virtual uint64_t confirmations() const = 0;
public Direction direction;
public boolean isPending;
public boolean isFailed;
@@ -62,6 +67,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
public String hash;
public long timestamp;
public String paymentId;
public int account;
public int subaddress;
public long confirmations;
public List<Transfer> transfers;
@@ -78,6 +85,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
String hash,
long timestamp,
String paymentId,
int account,
int subaddress,
long confirmations,
List<Transfer> transfers) {
this.direction = Direction.values()[direction];
@@ -89,6 +98,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
this.hash = hash;
this.timestamp = timestamp;
this.paymentId = paymentId;
this.account = account;
this.subaddress = subaddress;
this.confirmations = confirmations;
this.transfers = transfers;
}
@@ -108,6 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(hash);
out.writeLong(timestamp);
out.writeString(paymentId);
out.writeInt(account);
out.writeInt(subaddress);
out.writeLong(confirmations);
out.writeList(transfers);
out.writeString(txKey);
@@ -134,6 +147,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
hash = in.readString();
timestamp = in.readLong();
paymentId = in.readString();
account = in.readInt();
subaddress = in.readInt();
confirmations = in.readLong();
transfers = in.readArrayList(Transfer.class.getClassLoader());
txKey = in.readString();

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