mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 00:53:36 +02:00
Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7fc2dc3ba1 | ||
![]() |
43204d64ef | ||
![]() |
1433143a39 | ||
![]() |
a9a78393a9 | ||
![]() |
fe7ab31050 | ||
![]() |
bf5ed793b3 | ||
![]() |
679bae5f42 | ||
![]() |
e3ccda910e | ||
![]() |
ae75a34977 | ||
![]() |
03efedf35c | ||
![]() |
403dbdf14f | ||
![]() |
0bf3c6f099 | ||
![]() |
1b0ac1c481 | ||
![]() |
dc95539fc1 | ||
![]() |
023fb9e215 | ||
![]() |
92728026c7 | ||
![]() |
8fd9598c6c | ||
![]() |
6633261ba2 | ||
![]() |
dc8c8634cb | ||
![]() |
dcf9b6db15 | ||
![]() |
654d63c32e | ||
![]() |
caf91fccfd | ||
![]() |
41f1f3dec0 | ||
![]() |
bb66d1f68d | ||
![]() |
310548b031 | ||
![]() |
ad7737475f | ||
![]() |
b2d07a65b7 | ||
![]() |
5dfcaae5b9 | ||
![]() |
3c91fc060c | ||
![]() |
f8f113faab | ||
![]() |
268a00cb3e | ||
![]() |
878500ae71 | ||
![]() |
a0debb0f7e | ||
![]() |
3a71e8d352 | ||
![]() |
af68c5e51f | ||
![]() |
5a2b48a087 | ||
![]() |
0776b7b6a3 | ||
![]() |
c2eed85a83 | ||
![]() |
d7c2b4a727 | ||
![]() |
085e41f5da | ||
![]() |
6c6b3061a8 | ||
![]() |
5fc15779b7 | ||
![]() |
520d151f3c | ||
![]() |
7aad941dab | ||
![]() |
01e7693425 | ||
![]() |
091538752b | ||
![]() |
d5b95dd976 | ||
![]() |
008f06959c | ||
![]() |
817816cd34 | ||
![]() |
9d41d5da52 | ||
![]() |
ad76a7ffc1 | ||
![]() |
475542c4f3 | ||
![]() |
b5d0659ca9 | ||
![]() |
781bfbc78b | ||
![]() |
8985511209 | ||
![]() |
3c8a4ce967 | ||
![]() |
fcfedbcfae | ||
![]() |
74279b135a | ||
![]() |
d6d2de8312 | ||
![]() |
af0ecb2894 | ||
![]() |
975cc4f43c | ||
![]() |
74ba36de26 | ||
![]() |
7627e15a48 | ||
![]() |
37244cb9e0 | ||
![]() |
843566b820 | ||
![]() |
0bcf156929 | ||
![]() |
b1d91e2671 | ||
![]() |
271cd2d4a8 | ||
![]() |
22c5a543db | ||
![]() |
cd986860c5 | ||
![]() |
0cf5981eae | ||
![]() |
e109df34f0 | ||
![]() |
5a7aa6cc77 |
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 93
|
||||
versionName "1.5.3 'CrAzY 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 ->
|
||||
|
@@ -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
@@ -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
|
||||
}
|
||||
|
46
app/src/main/cpp/monerujo_ledger.h
Normal file
46
app/src/main/cpp/monerujo_ledger.h
Normal 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
|
53
app/src/main/java/com/btchip/BTChipException.java
Normal file
53
app/src/main/java/com/btchip/BTChipException.java
Normal 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;
|
||||
|
||||
}
|
31
app/src/main/java/com/btchip/comm/BTChipTransport.java
Normal file
31
app/src/main/java/com/btchip/comm/BTChipTransport.java
Normal 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);
|
||||
}
|
126
app/src/main/java/com/btchip/comm/LedgerHelper.java
Normal file
126
app/src/main/java/com/btchip/comm/LedgerHelper.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
62
app/src/main/java/com/btchip/utils/Dump.java
Normal file
62
app/src/main/java/com/btchip/utils/Dump.java
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
294
app/src/main/java/com/m2049r/xmrwallet/BaseActivity.java
Normal file
294
app/src/main/java/com/m2049r/xmrwallet/BaseActivity.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
@@ -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,13 +102,16 @@ 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);
|
||||
|
||||
boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
|
||||
tvWalletMnemonic.setTextIsSelectable(testnet);
|
||||
tvWalletSpendKey.setTextIsSelectable(testnet);
|
||||
tvWalletPassword.setTextIsSelectable(testnet);
|
||||
boolean allowCopy = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
|
||||
tvWalletMnemonic.setTextIsSelectable(allowCopy);
|
||||
tvWalletSpendKey.setTextIsSelectable(allowCopy);
|
||||
tvWalletPassword.setTextIsSelectable(allowCopy);
|
||||
|
||||
bAccept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
@@ -233,7 +239,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;
|
||||
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
142
app/src/main/java/com/m2049r/xmrwallet/ledger/Instruction.java
Normal file
142
app/src/main/java/com/m2049r/xmrwallet/ledger/Instruction.java
Normal 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;
|
||||
}
|
||||
}
|
272
app/src/main/java/com/m2049r/xmrwallet/ledger/Ledger.java
Normal file
272
app/src/main/java/com/m2049r/xmrwallet/ledger/Ledger.java
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user