1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-05 09:58:42 +02:00

Compare commits

..

31 Commits

Author SHA1 Message Date
m2049r
60a7b15700 new version for fixed malloc (83dcd65) 2018-04-03 11:51:14 +02:00
m2049r
df2ff8b3b7 correct toolbar colour for testnet (#218) 2018-04-02 13:02:00 +02:00
m2049r
da8c8f80f1 new version for m2049r/monero 7e97e11 (#217) 2018-04-02 12:14:55 +02:00
m2049r
eda3de11fe added zxcvbn4j license (#216) 2018-04-01 13:23:03 +02:00
m2049r
7d7de14827 new version id for monero PR#3526 (#215) 2018-03-30 14:12:17 +02:00
m2049r
1c709da92c Update FAQ.md 2018-03-29 23:00:39 +02:00
m2049r
a9092497b2 changes for monero v0.12 (#214)
* new version id & name
* witness checksums
* build docs updated for v0.12
* remove binaries
* setenv HOME for ringdb to 'monero' in shared storage
* min ringsize 7
* remove boost_locale and zmq from build - don't need them for wallet_api
* splits for all archs
* throw IndexOutOfBounds in case the TX is empty
* donate, you ungrateful bastards! (removed donations to make google happy)
2018-03-29 22:35:31 +02:00
m2049r
b1f530e64a new version number (#208) 2018-03-13 22:31:32 +01:00
m2049r
8c086b77d3 Minor Bugfixes (#207)
* corrected spanish

* removed testcode

* fix issue #206
2018-03-06 18:15:23 +01:00
m2049r
c0fdfe87be build for x86 & arm in both 32 & 64 bits (#204) 2018-02-27 07:22:11 +01:00
m2049r
6cfd840283 fixed some bugs & reverted to boost 1.58 2018-02-26 09:46:20 +01:00
Miguel Botón
d80cde1136 Added build instructions for 64-bit (aarch64). (#172) 2018-02-26 08:42:22 +01:00
m2049r
bea4b06675 new version 2018-02-23 19:47:20 +01:00
m2049r
88bc33b5a4 added zxcvbn password score (#199)
* added zxcvbn password score

* changes messages & thresholds

change password label to passphrase
2018-02-23 19:32:21 +01:00
m2049r
2db36bb824 Enable Restore with Date instead of Height Only (#198)
* restore height class

* exact heights

* tweaks with DST

* some more spanish
2018-02-23 19:31:55 +01:00
m2049r
dd689b1883 fix for 2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy (#190) 2018-01-24 19:59:13 +01:00
m2049r
641abd13f5 split apk (#191) 2018-01-23 21:35:26 +01:00
Stephan Hagios
a0b3a7fe5d used background borderless for nicer ripple effect (#189) 2018-01-22 21:36:13 +01:00
Stephan Hagios
a0486f581f Added circleci yml (#188)
* Adjusted layout files with correct sizes.

* Added password visibility toggle

* PR suggestions

* circle ci yml added

* different container with ndk
2018-01-21 18:53:45 +01:00
m2049r
36161137ec correct spanish (#187) 2018-01-18 17:45:46 +01:00
m2049r
c9927edbd1 save on exit only with connected wallet (#186) 2018-01-18 17:23:35 +01:00
m2049r
f0a3c05a9a v1.3.8 (#185) 2018-01-18 00:16:02 +01:00
m2049r
d6eb82c457 new wallet password fix (#184)
* revert to old (in-place) wallet creation method

* recover with password
2018-01-18 00:14:07 +01:00
m2049r
7f47307307 Revert "APK Split" (#183) 2018-01-16 22:23:10 +01:00
m2049r
165dd3dfd1 more spanish (#182) 2018-01-16 22:09:35 +01:00
m2049r
171727c9db new debug icon (#181) 2018-01-16 19:29:42 +01:00
m2049r
1492814ec9 new version 1.3.7 2018-01-16 19:28:30 +01:00
Stephan Hagios
9b1225fe4b Added an application suffix to distinguish between release and debug ver (#177)
* Added an application suffix to distinguish between release and debug ver

* Added icon for debug version.

* Added icon from mattermost assets channel.
2018-01-16 18:55:36 +01:00
m2049r
595d88e42e fixes & touchups (#178) 2018-01-15 23:31:14 +01:00
Stephan Hagios
0e207d7401 Merge pull request #174 from m2049r/feature/apk-split
APK Split
2018-01-04 17:07:52 +01:00
Stephan
5920d6c9e8 added apk split for different abis 2018-01-03 15:18:34 +01:00
99 changed files with 1796 additions and 670 deletions

27
.circleci/config.yml Normal file
View File

@@ -0,0 +1,27 @@
version: 2
jobs:
build:
working_directory: ~/code
docker:
- image: bitriseio/android-ndk
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Download Dependencies
command: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Tests
command: ./gradlew test
- store_artifacts:
path: app/build/reports
destination: reports
- store_test_results:
path: app/build/test-results

View File

@@ -60,9 +60,13 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a) ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
############# #############
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct) # Monero
############# #############
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
add_library(wallet STATIC IMPORTED) add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
@@ -91,11 +95,9 @@ add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
##### add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
add_library(p2p STATIC IMPORTED) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
add_library(blockchain_db STATIC IMPORTED) add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
@@ -113,7 +115,6 @@ add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
####
add_library(epee STATIC IMPORTED) add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
@@ -122,9 +123,21 @@ add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
add_library(miniupnpc STATIC IMPORTED) add_library(checkpoints STATIC IMPORTED)
set_target_properties(miniupnpc PROPERTIES IMPORTED_LOCATION set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libminiupnpc.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
############# #############
# System # System
@@ -137,23 +150,26 @@ include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
target_link_libraries( monerujo target_link_libraries( monerujo
wallet_api
wallet wallet
cryptonote_core cryptonote_core
cryptonote_basic cryptonote_basic
mnemonics mnemonics
ringct ringct
ringct_basic
common common
cncrypto cncrypto
blockchain_db blockchain_db
lmdb lmdb
easylogging easylogging
unbound unbound
p2p
epee epee
blocks blocks
miniupnpc checkpoints
device
multisig
version
boost_chrono boost_chrono
boost_date_time boost_date_time

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 25 targetSdkVersion 25
versionCode 65 versionCode 86
versionName "1.3.5 'Satoshis Dream'" versionName "1.4.6 'Monero Spedner'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@@ -18,7 +18,7 @@ android {
} }
} }
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
} }
} }
@@ -27,12 +27,37 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
debug {
applicationIdSuffix ".debug"
}
} }
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path "CMakeLists.txt" path "CMakeLists.txt"
} }
} }
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
// Map for the version code that gives each ABI a value.
def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.each {
output ->
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
}
}
} }
dependencies { dependencies {
@@ -47,6 +72,8 @@ dependencies {
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion" compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion" compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
compile 'com.nulab-inc:zxcvbn:1.2.3'
testCompile "junit:junit:$rootProject.ext.junitVersion" testCompile "junit:junit:$rootProject.ext.junitVersion"
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion" testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
@@ -77,5 +104,7 @@ dependencyVerification {
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d', 'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850', 'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4', 'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
'com.jakewharton.timber:timber:35c22867f2673132e97e17857d36bb2fc25f5790f0425406833ed0254d62fc66',
'com.nulab-inc:zxcvbn:18d7862a6abd2705defec478d77dedadf8f3bb7cf811df22995494f05485785f',
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">monerujo - Debug</string>
</resources>

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,9 @@ import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@@ -32,12 +34,18 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.Zxcvbn;
import java.io.File; import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import timber.log.Timber; import timber.log.Timber;
@@ -125,7 +133,7 @@ public class GenerateFragment extends Fragment {
}); });
Helper.showKeyboard(getActivity()); Helper.showKeyboard(getActivity());
//##############
etWalletName.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { etWalletName.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
@@ -231,9 +239,7 @@ public class GenerateFragment extends Fragment {
} }
if (!type.equals(TYPE_NEW)) { if (!type.equals(TYPE_NEW)) {
etWalletRestoreHeight.setVisibility(View.VISIBLE); etWalletRestoreHeight.setVisibility(View.VISIBLE);
etWalletRestoreHeight.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() etWalletRestoreHeight.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
{
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
Helper.hideKeyboard(getActivity()); Helper.hideKeyboard(getActivity());
@@ -254,11 +260,61 @@ public class GenerateFragment extends Fragment {
} }
}); });
etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
checkPassword();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
etWalletName.requestFocus(); etWalletName.requestFocus();
initZxcvbn();
return view; return view;
} }
Zxcvbn zxcvbn = new Zxcvbn();
// initialize zxcvbn engine in background thread
private void initZxcvbn() {
new Thread(new Runnable() {
@Override
public void run() {
zxcvbn.measure("");
}
}).start();
}
private void checkPassword() {
String password = etWalletPassword.getEditText().getText().toString();
if (!password.isEmpty()) {
Strength strength = zxcvbn.measure(password);
int msg;
double guessesLog10 = strength.getGuessesLog10();
if (guessesLog10 < 10)
msg = R.string.password_weak;
else if (guessesLog10 < 11)
msg = R.string.password_fair;
else if (guessesLog10 < 12)
msg = R.string.password_good;
else if (guessesLog10 < 13)
msg = R.string.password_strong;
else
msg = R.string.password_very_strong;
etWalletPassword.setError(getResources().getString(msg));
} else {
etWalletPassword.setError(null);
}
}
private boolean checkName() { private boolean checkName() {
String name = etWalletName.getEditText().getText().toString(); String name = etWalletName.getEditText().getText().toString();
boolean ok = true; boolean ok = true;
@@ -281,6 +337,42 @@ public class GenerateFragment extends Fragment {
return ok; return ok;
} }
private boolean checkHeight() {
long height = !type.equals(TYPE_NEW) ? getHeight() : 0;
boolean ok = true;
if (height < 0) {
etWalletRestoreHeight.setError(getString(R.string.generate_restoreheight_error));
ok = false;
}
if (ok) {
etWalletRestoreHeight.setError(null);
}
return ok;
}
private long getHeight() {
long height = 0;
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
if (restoreHeight.isEmpty()) return -1;
try {
// 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) {
try {
// or is it a height?
height = Long.parseLong(restoreHeight);
} catch (NumberFormatException exNFE) {
return -1;
}
}
Timber.d("Using Restore Height = %d", height);
return height;
}
private boolean checkMnemonic() { private boolean checkMnemonic() {
String seed = etWalletMnemonic.getEditText().getText().toString(); String seed = etWalletMnemonic.getEditText().getText().toString();
boolean ok = (seed.split("\\s").length == 25); // 25 words boolean ok = (seed.split("\\s").length == 25); // 25 words
@@ -294,7 +386,7 @@ public class GenerateFragment extends Fragment {
private boolean checkAddress() { private boolean checkAddress() {
String address = etWalletAddress.getEditText().getText().toString(); String address = etWalletAddress.getEditText().getText().toString();
boolean ok = Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet()); boolean ok = Wallet.isAddressValid(address);
if (!ok) { if (!ok) {
etWalletAddress.setError(getString(R.string.generate_check_address)); etWalletAddress.setError(getString(R.string.generate_check_address));
} else { } else {
@@ -327,15 +419,13 @@ public class GenerateFragment extends Fragment {
private void generateWallet() { private void generateWallet() {
if (!checkName()) return; if (!checkName()) return;
if (!checkHeight()) return;
String name = etWalletName.getEditText().getText().toString(); String name = etWalletName.getEditText().getText().toString();
String password = etWalletPassword.getEditText().getText().toString(); String password = etWalletPassword.getEditText().getText().toString();
long height; long height = getHeight();
try { if (height < 0) height = 0;
height = Long.parseLong(etWalletRestoreHeight.getEditText().getText().toString());
} catch (NumberFormatException ex) {
height = 0; // Keep calm and carry on!
}
if (type.equals(TYPE_NEW)) { if (type.equals(TYPE_NEW)) {
bGenerate.setEnabled(false); bGenerate.setEnabled(false);

View File

@@ -34,6 +34,7 @@ import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
@@ -81,7 +82,7 @@ public class GenerateReviewFragment extends Fragment {
bAccept = (Button) view.findViewById(R.id.bAccept); bAccept = (Button) view.findViewById(R.id.bAccept);
boolean testnet = WalletManager.getInstance().isTestNet(); boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
tvWalletMnemonic.setTextIsSelectable(testnet); tvWalletMnemonic.setTextIsSelectable(testnet);
tvWalletSpendKey.setTextIsSelectable(testnet); tvWalletSpendKey.setTextIsSelectable(testnet);
@@ -185,6 +186,7 @@ public class GenerateReviewFragment extends Fragment {
name = wallet.getName(); name = wallet.getName();
status = wallet.getStatus(); status = wallet.getStatus();
if (status != Wallet.Status.Status_Ok) { if (status != Wallet.Status.Status_Ok) {
Timber.e(wallet.getErrorString());
if (closeWallet) wallet.close(); if (closeWallet) wallet.close();
return false; return false;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -300,7 +300,7 @@ public class ReceiveFragment extends Fragment {
String paymentId = etPaymentId.getEditText().getText().toString(); String paymentId = etPaymentId.getEditText().getText().toString();
String xmrAmount = evAmount.getAmount(); String xmrAmount = evAmount.getAmount();
Timber.d("%s/%s/%s", xmrAmount, paymentId, address); Timber.d("%s/%s/%s", xmrAmount, paymentId, address);
if ((xmrAmount == null) || !Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) { if ((xmrAmount == null) || !Wallet.isAddressValid(address)) {
clearQR(); clearQR();
Timber.d("CLEARQR"); Timber.d("CLEARQR");
return; return;

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
@@ -35,7 +36,7 @@ import android.widget.Toast;
import com.m2049r.xmrwallet.data.BarcodeData; import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.dialog.DonationFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment; import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
import com.m2049r.xmrwallet.fragment.send.SendFragment; import com.m2049r.xmrwallet.fragment.send.SendFragment;
@@ -140,6 +141,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
protected void onDestroy() { protected void onDestroy() {
Timber.d("onDestroy()"); Timber.d("onDestroy()");
if ((mBoundService != null) && !isSynced() && (getWallet() != null)) {
saveWallet();
}
stopWalletService(); stopWalletService();
super.onDestroy(); super.onDestroy();
} }
@@ -155,8 +159,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case R.id.action_info: case R.id.action_info:
onWalletDetails(); onWalletDetails();
return true; return true;
case R.id.action_donate: case R.id.action_credits:
DonationFragment.display(getSupportFragmentManager()); CreditsFragment.display(getSupportFragmentManager());
return true; return true;
case R.id.action_share: case R.id.action_share:
onShareTxInfo(); onShareTxInfo();
@@ -209,8 +213,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_DONATE: case Toolbar.BUTTON_CREDITS:
Toast.makeText(WalletActivity.this, getString(R.string.label_donate), Toast.LENGTH_SHORT).show(); Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
default: default:
Timber.e("Button " + type + "pressed - how can this be?"); Timber.e("Button " + type + "pressed - how can this be?");
@@ -218,12 +222,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
}); });
boolean testnet = WalletManager.getInstance().isTestNet(); showNet();
if (testnet) {
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
} else {
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
}
Fragment walletFragment = new WalletFragment(); Fragment walletFragment = new WalletFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
@@ -234,6 +233,23 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
Timber.d("onCreate() done."); Timber.d("onCreate() done.");
} }
public void showNet() {
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
break;
case NetworkType_Testnet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
case NetworkType_Stagenet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
}
}
public Wallet getWallet() { public Wallet getWallet() {
if (mBoundService == null) throw new IllegalStateException("WalletService not bound."); if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
return mBoundService.getWallet(); return mBoundService.getWallet();
@@ -794,7 +810,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public boolean verifyWalletPassword(String password) { public boolean verifyWalletPassword(String password) {
String walletPath = new File(Helper.getStorageRoot(this), String walletPath = new File(Helper.getWalletRoot(this),
getWalletName() + ".keys").getAbsolutePath(); getWalletName() + ".keys").getAbsolutePath();
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
} }
@@ -807,9 +823,6 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
super.onBackPressed(); super.onBackPressed();
} }
} else { } else {
if (!isSynced()) {
saveWallet();
}
super.onBackPressed(); super.onBackPressed();
} }
} }
@@ -818,4 +831,10 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
public void onFragmentDone() { public void onFragmentDone() {
popFragmentStack(null); popFragmentStack(null);
} }
@Override
public SharedPreferences getPrefs() {
return getPreferences(Context.MODE_PRIVATE);
}
} }

View File

@@ -19,13 +19,15 @@ package com.m2049r.xmrwallet;
import android.app.Application; import android.app.Application;
import com.m2049r.xmrwallet.util.Helper;
import timber.log.Timber; import timber.log.Timber;
public class XmrWalletApplication extends Application { public class XmrWalletApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree()); Timber.plant(new Timber.DebugTree());
} }

View File

@@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
import android.net.Uri; import android.net.Uri;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.BitcoinAddressValidator; import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
@@ -178,7 +179,7 @@ public class BarcodeData {
return null; // we have an amount but its not a number! return null; // we have an amount but its not a number!
} }
} }
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }
@@ -190,7 +191,7 @@ public class BarcodeData {
if (address == null) return null; if (address == null) return null;
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }

View File

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

View File

@@ -28,16 +28,14 @@ import android.text.Html;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;
public class DonationFragment extends DialogFragment { public class CreditsFragment extends DialogFragment {
static final String TAG = "DonationFragment"; static final String TAG = "DonationFragment";
public static DonationFragment newInstance() { public static CreditsFragment newInstance() {
return new DonationFragment(); return new CreditsFragment();
} }
public static void display(FragmentManager fm) { public static void display(FragmentManager fm) {
@@ -47,24 +45,14 @@ public class DonationFragment extends DialogFragment {
ft.remove(prev); ft.remove(prev);
} }
DonationFragment.newInstance().show(ft, TAG); CreditsFragment.newInstance().show(ft, TAG);
} }
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_donation, null); final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_credits, null);
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.donation_credits))); ((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
(view.findViewById(R.id.bCopyAddress)).
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address),
((TextView) view.findViewById(R.id.tvWalletAddress)).getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view); builder.setView(view);

View File

@@ -212,7 +212,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean checkAddressNoError() { private boolean checkAddressNoError() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return Wallet.isAddressValid(address) return Wallet.isAddressValid(address)
|| BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); || BitcoinAddressValidator.validate(address);
} }
private boolean checkAddress() { private boolean checkAddress() {
@@ -228,13 +228,13 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean isIntegratedAddress() { private boolean isIntegratedAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return (address.length() == INTEGRATED_ADDRESS_LENGTH) return (address.length() == INTEGRATED_ADDRESS_LENGTH)
&& Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet()); && Wallet.isAddressValid(address);
} }
private boolean isBitcoinAddress() { private boolean isBitcoinAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
if ((address.length() >= 27) && (address.length() <= 34)) if ((address.length() >= 27) && (address.length() <= 34))
return BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); return BitcoinAddressValidator.validate(address);
else else
return false; return false;
} }

View File

@@ -17,6 +17,7 @@
package com.m2049r.xmrwallet.fragment.send; package com.m2049r.xmrwallet.fragment.send;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -41,9 +42,11 @@ import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.PendingTx; import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.TxDataBtc; import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.SpendViewPager; import com.m2049r.xmrwallet.layout.SpendViewPager;
import com.m2049r.xmrwallet.model.PendingTransaction; import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.NodeList;
import com.m2049r.xmrwallet.util.UserNotes; import com.m2049r.xmrwallet.util.UserNotes;
import com.m2049r.xmrwallet.widget.DotBar; import com.m2049r.xmrwallet.widget.DotBar;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
@@ -63,6 +66,8 @@ public class SendFragment extends Fragment
private Listener activityCallback; private Listener activityCallback;
public interface Listener { public interface Listener {
SharedPreferences getPrefs();
long getTotalFunds(); long getTotalFunds();
void onPrepareSend(String tag, TxData data); void onPrepareSend(String tag, TxData data);
@@ -93,6 +98,10 @@ public class SendFragment extends Fragment
private Button bDone; private Button bDone;
private View llXmrToEnabled;
private View ibXmrToInfoClose;
static private int MAX_FALLBACK = Integer.MAX_VALUE; static private int MAX_FALLBACK = Integer.MAX_VALUE;
@Override @Override
@@ -110,6 +119,28 @@ public class SendFragment extends Fragment
arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_white_24dp); arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_white_24dp);
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp); arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
llXmrToEnabled = view.findViewById(R.id.llXmrToEnabled);
llXmrToEnabled.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
HelpFragment.display(getChildFragmentManager(), R.string.help_xmrto);
}
});
ibXmrToInfoClose = view.findViewById(R.id.ibXmrToInfoClose);
ibXmrToInfoClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
llXmrToEnabled.setVisibility(View.GONE);
showXmrtoEnabled = false;
saveXmrToPrefs();
}
});
loadPrefs();
if (!showXmrtoEnabled) {
llXmrToEnabled.setVisibility(View.GONE);
}
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager); spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager()); pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
spendViewPager.setOffscreenPageLimit(pagerAdapter.getCount()); // load & keep all pages in cache spendViewPager.setOffscreenPageLimit(pagerAdapter.getCount()); // load & keep all pages in cache
@@ -518,4 +549,22 @@ public class SendFragment extends Fragment
inflater.inflate(R.menu.send_menu, menu); inflater.inflate(R.menu.send_menu, menu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
// xmr.to info box
private static final String PREF_SHOW_XMRTO_ENABLED = "info_xmrto_enabled_send";
boolean showXmrtoEnabled = true;
void loadPrefs() {
SharedPreferences sharedPref = activityCallback.getPrefs();
showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true);
}
void saveXmrToPrefs() {
SharedPreferences sharedPref = activityCallback.getPrefs();
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
editor.apply();
}
} }

View File

@@ -54,7 +54,8 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
TxData getTxData(); TxData getTxData();
} }
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML // Mixin = Ringsize - 1
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
final static PendingTransaction.Priority Priorities[] = final static PendingTransaction.Priority Priorities[] =
{PendingTransaction.Priority.Priority_Default, {PendingTransaction.Priority.Priority_Default,
PendingTransaction.Priority.Priority_Low, PendingTransaction.Priority.Priority_Low,

View File

@@ -77,7 +77,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString()); Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.message_copy_txid), Toast.LENGTH_SHORT).show();
} }
}); });

View File

@@ -130,12 +130,14 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
void bind(int position) { void bind(int position) {
this.infoItem = infoItems.get(position); this.infoItem = infoItems.get(position);
long realAmount = infoItem.amount; UserNotes userNotes = new UserNotes(infoItem.notes);
if (infoItem.isPending) { if (userNotes.xmrtoKey != null) {
realAmount = realAmount - infoItem.fee; ivTxType.setVisibility(View.VISIBLE);
} else {
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
} }
String displayAmount = Helper.getDisplayAmount(realAmount, Helper.DISPLAY_DIGITS_INFO); String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) { if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount)); tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
} else { } else {
@@ -163,13 +165,6 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
setTxColour(outboundColour); setTxColour(outboundColour);
} }
UserNotes userNotes = new UserNotes(infoItem.notes);
if (userNotes.xmrtoKey != null) {
ivTxType.setVisibility(View.VISIBLE);
} else {
ivTxType.setVisibility(View.INVISIBLE);
}
if ((userNotes.note.isEmpty())) { if ((userNotes.note.isEmpty())) {
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId); this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId);
} else { } else {

View File

@@ -0,0 +1,45 @@
/*
* 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.model;
public enum NetworkType {
NetworkType_Mainnet(0),
NetworkType_Testnet(1),
NetworkType_Stagenet(2);
public static NetworkType fromInteger(int n) {
switch (n) {
case 0:
return NetworkType_Mainnet;
case 1:
return NetworkType_Testnet;
case 2:
return NetworkType_Stagenet;
}
return null;
}
public int getValue() {
return value;
}
private int value;
NetworkType(int value) {
this.value = value;
}
}

View File

@@ -84,7 +84,14 @@ public class PendingTransaction {
public native long getFee(); public native long getFee();
public native String getFirstTxId(); public String getFirstTxId() {
String id = getFirstTxIdJ();
if (id == null)
throw new IndexOutOfBoundsException();
return id;
}
public native String getFirstTxIdJ();
public native long getTxCount(); public native long getTxCount();

View File

@@ -79,7 +79,11 @@ public class Wallet {
public native String getPath(); public native String getPath();
public native boolean isTestNet(); public NetworkType getNetworkType() {
return NetworkType.fromInteger(nettype());
}
public native int nettype();
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; //TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; //TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
@@ -155,10 +159,10 @@ public class Wallet {
public static native boolean isPaymentIdValid(String payment_id); public static native boolean isPaymentIdValid(String payment_id);
public static boolean isAddressValid(String address) { public static boolean isAddressValid(String address) {
return isAddressValid(address, WalletManager.getInstance().isTestNet()); return isAddressValid(address, WalletManager.getInstance().getNetworkType().getValue());
} }
public static native boolean isAddressValid(String address, boolean isTestNet); public static native boolean isAddressValid(String address, int networkType);
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); //TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);

View File

@@ -16,6 +16,8 @@
package com.m2049r.xmrwallet.model; package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.WalletNode;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
@@ -69,49 +71,51 @@ public class WalletManager {
} }
public Wallet createWallet(File aFile, String password, String language) { public Wallet createWallet(File aFile, String password, String language) {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet()); long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletJ(String path, String password, String language, boolean isTestNet); private native long createWalletJ(String path, String password, String language, int networkType);
public Wallet openWallet(String path, String password) { public Wallet openWallet(String path, String password) {
long walletHandle = openWalletJ(path, password, isTestNet()); long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long openWalletJ(String path, String password, boolean isTestNet); private native long openWalletJ(String path, String password, int networkType);
public Wallet recoveryWallet(File aFile, String mnemonic) { public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
Wallet wallet = recoveryWallet(aFile, mnemonic, 0); return recoveryWallet(aFile, password, mnemonic, 0);
manageWallet(wallet);
return wallet;
} }
public Wallet recoveryWallet(File aFile, String mnemonic, long restoreHeight) { public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), mnemonic, isTestNet(), restoreHeight); long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
getNetworkType().getValue(), restoreHeight);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight); private native long recoveryWalletJ(String path, String password, String mnemonic,
int networkType, long restoreHeight);
public Wallet createWalletFromKeys(File aFile, String language, long restoreHeight, public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) { String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), language, isTestNet(), restoreHeight, long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), password,
language, getNetworkType().getValue(), restoreHeight,
addressString, viewKeyString, spendKeyString); addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletFromKeysJ(String path, String language, private native long createWalletFromKeysJ(String path, String password,
boolean isTestNet, String language,
int networkType,
long restoreHeight, long restoreHeight,
String addressString, String addressString,
String viewKeyString, String viewKeyString,
@@ -202,24 +206,23 @@ public class WalletManager {
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0; //TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
private String daemonAddress = null; private String daemonAddress = null;
private boolean testnet = true; private NetworkType networkType = null;
public boolean isTestNet() { public NetworkType getNetworkType() {
if (daemonAddress == null) { return networkType;
return true;
// assume testnet not explicitly initialised
//throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return testnet;
} }
public void setDaemon(String address, boolean testnet, String username, String password) { //public void setDaemon(String address, NetworkType networkType, String username, String password) {
//Timber.d("SETDAEMON " + username + "/" + password + "/" + address); public void setDaemon(WalletNode walletNode) {
this.daemonAddress = address; this.daemonAddress = walletNode.getAddress();
this.testnet = testnet; this.networkType = networkType;
this.daemonUsername = username; this.daemonUsername = walletNode.getUsername();
this.daemonPassword = password; this.daemonPassword = walletNode.getPassword();
setDaemonAddressJ(address); setDaemonAddressJ(daemonAddress);
}
public void setNetworkType(NetworkType networkType) {
this.networkType = networkType;
} }
public String getDaemonAddress() { public String getDaemonAddress() {

View File

@@ -524,7 +524,7 @@ public class WalletService extends Service {
showProgress(20); showProgress(20);
Wallet wallet = null; Wallet wallet = null;
WalletManager walletMgr = WalletManager.getInstance(); WalletManager walletMgr = WalletManager.getInstance();
Timber.d("WalletManager testnet=%s", walletMgr.isTestNet()); Timber.d("WalletManager network=%s", walletMgr.getNetworkType().name());
showProgress(30); showProgress(30);
if (walletMgr.walletExists(path)) { if (walletMgr.walletExists(path)) {
Timber.d("open wallet %s", path); Timber.d("open wallet %s", path);

View File

@@ -18,6 +18,9 @@ package com.m2049r.xmrwallet.util;
// based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java // based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@@ -27,6 +30,11 @@ public class BitcoinAddressValidator {
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static boolean validate(String addrress) {
return validate(addrress,
WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet);
}
public static boolean validate(String addrress, boolean testnet) { public static boolean validate(String addrress, boolean testnet) {
if (addrress.length() < 26 || addrress.length() > 35) if (addrress.length() < 26 || addrress.length() > 35)
return false; return false;
@@ -34,10 +42,11 @@ public class BitcoinAddressValidator {
if (decoded == null) if (decoded == null)
return false; return false;
int v = decoded[0] & 0xFF;
if (!testnet) { if (!testnet) {
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false; if ((v != 0x00) && (v != 0x05)) return false;
} else { } else {
if ((decoded[0] != 0x6f) && (decoded[0] != 0xc4)) return false; if ((v != 0x6f) && (v != 0xc4)) return false;
} }
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21)); byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
@@ -57,7 +66,11 @@ public class BitcoinAddressValidator {
byte[] result = new byte[25]; byte[] result = new byte[25];
byte[] numBytes = num.toByteArray(); byte[] numBytes = num.toByteArray();
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length); if (num.bitLength() == 200) {
System.arraycopy(numBytes, 1, result, 0, 25);
} else {
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
}
return result; return result;
} }

View File

@@ -31,12 +31,15 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable; import android.graphics.drawable.VectorDrawable;
import android.os.Environment; import android.os.Environment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.system.ErrnoException;
import android.system.Os;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
@@ -55,27 +58,21 @@ import timber.log.Timber;
public class Helper { public class Helper {
static private final String WALLET_DIR = "monerujo"; static private final String WALLET_DIR = "monerujo";
static private final String HOME_DIR = "monero";
static public int DISPLAY_DIGITS_INFO = 5; static public int DISPLAY_DIGITS_INFO = 5;
static public File getNewWalletDir(Context context) { static public File getWalletRoot(Context context) {
File newWalletDir = context.getDir("new", Context.MODE_PRIVATE); return getStorage(context, WALLET_DIR);
Timber.d("new wallet directory is %s", newWalletDir.getAbsolutePath());
if (!newWalletDir.exists() || !newWalletDir.isDirectory()) {
String msg = newWalletDir.getAbsolutePath() + " is not a directory!";
Timber.e(msg);
throw new IllegalStateException(msg);
}
return newWalletDir;
} }
static public File getStorageRoot(Context context) { static public File getStorage(Context context, String folderName) {
if (!isExternalStorageWritable()) { if (!isExternalStorageWritable()) {
String msg = context.getString(R.string.message_strorage_not_writable); String msg = context.getString(R.string.message_strorage_not_writable);
Timber.e(msg); Timber.e(msg);
throw new IllegalStateException(msg); throw new IllegalStateException(msg);
} }
File dir = new File(Environment.getExternalStorageDirectory(), WALLET_DIR); File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists()) { if (!dir.exists()) {
Timber.i("Creating %s", dir.getAbsolutePath()); Timber.i("Creating %s", dir.getAbsolutePath());
dir.mkdirs(); // try to make it dir.mkdirs(); // try to make it
@@ -125,9 +122,9 @@ public class Helper {
} }
static public File getWalletFile(Context context, String aWalletName) { static public File getWalletFile(Context context, String aWalletName) {
File walletDir = getStorageRoot(context); File walletDir = getWalletRoot(context);
File f = new File(walletDir, aWalletName); File f = new File(walletDir, aWalletName);
Timber.d("wallet= %s size= %d", f.getAbsolutePath(), f.length()); Timber.d("wallet=%s size= %d", f.getAbsolutePath(), f.length());
return f; return f;
} }
@@ -274,10 +271,20 @@ public class Helper {
} }
static public HttpUrl getXmrToBaseUrl() { static public HttpUrl getXmrToBaseUrl() {
if ((WalletManager.getInstance() == null) || WalletManager.getInstance().isTestNet()) { if ((WalletManager.getInstance() == null)
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/");
} else { } else {
return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/");
} }
} }
static public void setMoneroHome(Context context) {
try {
String home = getStorage(context, HOME_DIR).getAbsolutePath();
Os.setenv("HOME", home, true);
} catch (ErrnoException ex) {
throw new IllegalStateException(ex);
}
}
} }

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