1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-08 20:40:51 +02:00

Compare commits

...

24 Commits

Author SHA1 Message Date
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
151 changed files with 980 additions and 256 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

@@ -8,8 +8,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 65
versionName "1.3.5 'Satoshis Dream'"
versionCode 74
versionName "1.3.14 'Satoshis Dream'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -18,7 +18,7 @@ android {
}
}
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
@@ -27,12 +27,37 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk false
}
}
// 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 {
@@ -47,6 +72,8 @@ dependencies {
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
compile 'com.nulab-inc:zxcvbn:1.2.3'
testCompile "junit:junit:$rootProject.ext.junitVersion"
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"

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>

View File

@@ -291,41 +291,48 @@ Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject i
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance,
jstring path, jstring mnemonic,
jstring path, jstring password,
jstring mnemonic,
jboolean isTestNet,
jlong restoreHeight) {
const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL);
const char *_mnemonic = env->GetStringUTFChars(mnemonic, NULL);
Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->recoveryWallet(
std::string(_path),
std::string(_password),
std::string(_mnemonic),
isTestNet,
restoreHeight);
env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password);
env->ReleaseStringUTFChars(mnemonic, _mnemonic);
return reinterpret_cast<jlong>(wallet);
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance,
jstring path, jstring language,
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletWithKeysJ(JNIEnv *env, jobject instance,
jstring path, jstring password,
jstring language,
jboolean isTestNet,
jlong restoreHeight,
jstring addressString,
jstring viewKeyString,
jstring spendKeyString) {
const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL);
const char *_language = env->GetStringUTFChars(language, NULL);
const char *_addressString = env->GetStringUTFChars(addressString, NULL);
const char *_viewKeyString = env->GetStringUTFChars(viewKeyString, NULL);
const char *_spendKeyString = env->GetStringUTFChars(spendKeyString, NULL);
Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->createWalletFromKeys(
Bitmonero::WalletManagerFactory::getWalletManager()->createWalletWithKeys(
std::string(_path),
std::string(_password),
std::string(_language),
isTestNet,
restoreHeight,
@@ -334,6 +341,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env,
std::string(_spendKeyString));
env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password);
env->ReleaseStringUTFChars(language, _language);
env->ReleaseStringUTFChars(addressString, _addressString);
env->ReleaseStringUTFChars(viewKeyString, _viewKeyString);

View File

@@ -21,7 +21,9 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -32,12 +34,18 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.Zxcvbn;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import timber.log.Timber;
@@ -125,7 +133,7 @@ public class GenerateFragment extends Fragment {
});
Helper.showKeyboard(getActivity());
//##############
etWalletName.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_NEXT)) {
@@ -231,9 +239,7 @@ public class GenerateFragment extends Fragment {
}
if (!type.equals(TYPE_NEW)) {
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) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
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();
initZxcvbn();
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() {
String name = etWalletName.getEditText().getText().toString();
boolean ok = true;
@@ -281,6 +337,42 @@ public class GenerateFragment extends Fragment {
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() {
String seed = etWalletMnemonic.getEditText().getText().toString();
boolean ok = (seed.split("\\s").length == 25); // 25 words
@@ -327,15 +419,13 @@ public class GenerateFragment extends Fragment {
private void generateWallet() {
if (!checkName()) return;
if (!checkHeight()) return;
String name = etWalletName.getEditText().getText().toString();
String password = etWalletPassword.getEditText().getText().toString();
long height;
try {
height = Long.parseLong(etWalletRestoreHeight.getEditText().getText().toString());
} catch (NumberFormatException ex) {
height = 0; // Keep calm and carry on!
}
long height = getHeight();
if (height < 0) height = 0;
if (type.equals(TYPE_NEW)) {
bGenerate.setEnabled(false);

View File

@@ -778,13 +778,14 @@ public class LoginActivity extends SecureActivity
static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more
private class AsyncCreateWallet extends AsyncTask<Void, Void, Boolean> {
String walletName;
String walletPassword;
WalletCreator walletCreator;
final String walletName;
final String walletPassword;
final WalletCreator walletCreator;
File newWalletFile;
public AsyncCreateWallet(String name, String password, WalletCreator walletCreator) {
public AsyncCreateWallet(final String name, final String password,
final WalletCreator walletCreator) {
super();
this.walletName = name;
this.walletPassword = password;
@@ -814,12 +815,7 @@ public class LoginActivity extends SecureActivity
return false;
}
File newWalletFolder = Helper.getNewWalletDir(getApplicationContext());
if (!newWalletFolder.isDirectory()) {
Timber.e("New Wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory");
return false;
}
newWalletFile = new File(newWalletFolder, walletName);
newWalletFile = new File(walletFolder, walletName);
boolean success = walletCreator.createWallet(newWalletFile, walletPassword);
if (success) {
return true;
@@ -844,7 +840,8 @@ public class LoginActivity extends SecureActivity
}
}
public void createWallet(String name, String password, WalletCreator walletCreator) {
public void createWallet(final String name, final String password,
final WalletCreator walletCreator) {
new AsyncCreateWallet(name, password, walletCreator)
.executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
}
@@ -865,7 +862,7 @@ public class LoginActivity extends SecureActivity
}
@Override
public void onGenerate(String name, String password) {
public void onGenerate(final String name, final String password) {
createWallet(name, password,
new WalletCreator() {
public boolean createWallet(File aFile, String password) {
@@ -883,16 +880,15 @@ public class LoginActivity extends SecureActivity
}
@Override
public void onGenerate(String name, String password, final String seed, final long restoreHeight) {
public void onGenerate(final String name, final String password, final String seed,
final long restoreHeight) {
createWallet(name, password,
new WalletCreator() {
public boolean createWallet(File aFile, String password) {
Wallet newWallet = WalletManager.getInstance().recoveryWallet(aFile, seed, restoreHeight);
Wallet newWallet = WalletManager.getInstance().
recoveryWallet(aFile, password, seed, restoreHeight);
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (success) {
newWallet.setPassword(password);
success = success && newWallet.store();
} else {
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
@@ -903,19 +899,17 @@ public class LoginActivity extends SecureActivity
}
@Override
public void onGenerate(String name, String password,
final String address, final String viewKey, final String spendKey, final long restoreHeight) {
public void onGenerate(final String name, final String password,
final String address, final String viewKey, final String spendKey,
final long restoreHeight) {
createWallet(name, password,
new WalletCreator() {
public boolean createWallet(File aFile, String password) {
Wallet newWallet = WalletManager.getInstance()
.createWalletFromKeys(aFile, MNEMONIC_LANGUAGE, restoreHeight,
.createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight,
address, viewKey, spendKey);
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
if (success) {
newWallet.setPassword(password);
success = success && newWallet.store();
} else {
if (!success) {
Timber.e(newWallet.getErrorString());
toast(newWallet.getErrorString());
}
@@ -936,16 +930,10 @@ public class LoginActivity extends SecureActivity
@Override
public void onAccept(final String name, final String password) {
File newWalletFile = new File(Helper.getNewWalletDir(getApplicationContext()), name);
Timber.d("New Wallet %s", newWalletFile.getAbsolutePath());
newWalletFile.delete(); // when recovering wallets, the cache seems corrupt
// TODO: figure out why this is so? Only for a private testnet?
// now copy the new wallet to the wallet folder
File walletFile = new File(getStorageRoot(), name);
Timber.d("Wallet %s", walletFile.getAbsolutePath());
copyWallet(newWalletFile, walletFile, false, true);
deleteWallet(newWalletFile); // delete it no matter what (can't recover from this anyway)
File walletFolder = getStorageRoot();
File walletFile = new File(walletFolder, name);
Timber.d("New Wallet %s", walletFile.getAbsolutePath());
walletFile.delete(); // when recovering wallets, the cache seems corrupt
boolean rc = testWallet(walletFile.getAbsolutePath(), password) == Wallet.Status.Status_Ok;

View File

@@ -43,6 +43,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
@@ -61,15 +62,18 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private WalletInfoAdapter adapter;
List<WalletManager.WalletInfo> walletList = new ArrayList<>();
List<WalletManager.WalletInfo> displayedList = new ArrayList<>();
private List<WalletManager.WalletInfo> walletList = new ArrayList<>();
private List<WalletManager.WalletInfo> displayedList = new ArrayList<>();
EditText etDummy;
ImageView ivGunther;
DropDownEditText etDaemonAddress;
ArrayAdapter<String> nodeAdapter;
private EditText etDummy;
private ImageView ivGunther;
private DropDownEditText etDaemonAddress;
private ArrayAdapter<String> nodeAdapter;
Listener activityCallback;
private View llXmrToEnabled;
private View ibXmrToInfoClose;
private Listener activityCallback;
// Container Activity must implement this interface
public interface Listener {
@@ -165,6 +169,25 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
recyclerView.setAdapter(adapter);
etDummy = (EditText) view.findViewById(R.id.etDummy);
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();
}
});
etDaemonAddress = (DropDownEditText) view.findViewById(R.id.etDaemonAddress);
nodeAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line);
etDaemonAddress.setAdapter(nodeAdapter);
@@ -211,6 +234,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
});
loadPrefs();
if (!showXmrtoEnabled) {
llXmrToEnabled.setVisibility(View.GONE);
}
return view;
}
@@ -290,9 +316,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
activityCallback.onWalletDetails(name, isTestnet());
}
private boolean showReceive(@NonNull String name) {
private void showReceive(@NonNull String name) {
activityCallback.onWalletReceive(name, isTestnet());
return true;
}
@Override
@@ -308,7 +333,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
super.onCreateOptionsMenu(menu, inflater);
}
boolean testnet = false;
private boolean testnet = BuildConfig.DEBUG;
boolean isTestnet() {
return testnet;
@@ -336,6 +361,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private static final String PREF_DAEMON_TESTNET = "daemon_testnet";
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
private static final String PREF_SHOW_XMRTO_ENABLED = "info_xmrto_enabled_login";
private static final String PREF_DAEMONLIST_MAINNET =
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
@@ -346,22 +372,33 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private NodeList daemonTestNet;
private NodeList daemonMainNet;
boolean showXmrtoEnabled = true;
void loadPrefs() {
SharedPreferences sharedPref = activityCallback.getPrefs();
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
setNet(isTestnet(), false);
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();
}
void savePrefs() {
savePrefs(false);
}
void savePrefs(boolean usePreviousState) {
Timber.d("SAVE / %s", usePreviousState);
void savePrefs(boolean usePreviousTestnetState) {
Timber.d("SAVE / %s", usePreviousTestnetState);
// save the daemon address for the net
boolean testnet = isTestnet() ^ usePreviousState;
boolean testnet = isTestnet() ^ usePreviousTestnetState;
String daemon = getDaemon();
if (testnet) {
daemonTestNet.setRecent(daemon);
@@ -373,6 +410,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(PREF_DAEMON_MAINNET, daemonMainNet.toString());
editor.putString(PREF_DAEMON_TESTNET, daemonTestNet.toString());
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
editor.apply();
}

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
@@ -140,6 +141,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override
protected void onDestroy() {
Timber.d("onDestroy()");
if ((mBoundService != null) && !isSynced() && (getWallet() != null)) {
saveWallet();
}
stopWalletService();
super.onDestroy();
}
@@ -807,9 +811,6 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
super.onBackPressed();
}
} else {
if (!isSynced()) {
saveWallet();
}
super.onBackPressed();
}
}
@@ -818,4 +819,10 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
public void onFragmentDone() {
popFragmentStack(null);
}
@Override
public SharedPreferences getPrefs() {
return getPreferences(Context.MODE_PRIVATE);
}
}

View File

@@ -17,6 +17,7 @@
package com.m2049r.xmrwallet.fragment.send;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
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.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.UserNotes;
import com.m2049r.xmrwallet.widget.DotBar;
import com.m2049r.xmrwallet.widget.Toolbar;
@@ -63,6 +66,8 @@ public class SendFragment extends Fragment
private Listener activityCallback;
public interface Listener {
SharedPreferences getPrefs();
long getTotalFunds();
void onPrepareSend(String tag, TxData data);
@@ -93,6 +98,10 @@ public class SendFragment extends Fragment
private Button bDone;
private View llXmrToEnabled;
private View ibXmrToInfoClose;
static private int MAX_FALLBACK = Integer.MAX_VALUE;
@Override
@@ -110,6 +119,28 @@ public class SendFragment extends Fragment
arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_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);
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
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);
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

@@ -77,7 +77,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
@Override
public void onClick(View v) {
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,6 +130,13 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
void bind(int position) {
this.infoItem = infoItems.get(position);
UserNotes userNotes = new UserNotes(infoItem.notes);
if (userNotes.xmrtoKey != null) {
ivTxType.setVisibility(View.VISIBLE);
} else {
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
}
long realAmount = infoItem.amount;
if (infoItem.isPending) {
realAmount = realAmount - infoItem.fee;
@@ -163,13 +170,6 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
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())) {
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId);
} else {

View File

@@ -86,31 +86,33 @@ public class WalletManager {
private native long openWalletJ(String path, String password, boolean isTestNet);
public Wallet recoveryWallet(File aFile, String mnemonic) {
Wallet wallet = recoveryWallet(aFile, mnemonic, 0);
manageWallet(wallet);
return wallet;
public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
return recoveryWallet(aFile, password, mnemonic, 0);
}
public Wallet recoveryWallet(File aFile, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), mnemonic, isTestNet(), restoreHeight);
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
isTestNet(), restoreHeight);
Wallet wallet = new Wallet(walletHandle);
manageWallet(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,
boolean isTestNet, 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) {
long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), language, isTestNet(), restoreHeight,
long walletHandle = createWalletWithKeysJ(aFile.getAbsolutePath(), password,
language, isTestNet(), restoreHeight,
addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet);
return wallet;
}
private native long createWalletFromKeysJ(String path, String language,
private native long createWalletWithKeysJ(String path, String password,
String language,
boolean isTestNet,
long restoreHeight,
String addressString,

View File

@@ -34,10 +34,11 @@ public class BitcoinAddressValidator {
if (decoded == null)
return false;
int v = decoded[0] & 0xFF;
if (!testnet) {
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false;
if ((v != 0x00) && (v != 0x05)) return false;
} 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));
@@ -57,7 +58,11 @@ public class BitcoinAddressValidator {
byte[] result = new byte[25];
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;
}

View File

@@ -58,17 +58,6 @@ public class Helper {
static public int DISPLAY_DIGITS_INFO = 5;
static public File getNewWalletDir(Context context) {
File newWalletDir = context.getDir("new", Context.MODE_PRIVATE);
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) {
if (!isExternalStorageWritable()) {
String msg = context.getString(R.string.message_strorage_not_writable);

View File

@@ -0,0 +1,152 @@
/*
* 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.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
public class RestoreHeight {
static private RestoreHeight Singleton = null;
static public RestoreHeight getInstance() {
if (Singleton == null) {
synchronized (RestoreHeight.class) {
if (Singleton == null) {
Singleton = new RestoreHeight();
}
}
}
return Singleton;
}
private Map<String, Long> blockheight = new HashMap<>();
RestoreHeight() {
blockheight.put("2014-05-01", 18844L);
blockheight.put("2014-06-01", 65406L);
blockheight.put("2014-07-01", 108882L);
blockheight.put("2014-08-01", 153594L);
blockheight.put("2014-09-01", 198072L);
blockheight.put("2014-10-01", 241088L);
blockheight.put("2014-11-01", 285305L);
blockheight.put("2014-12-01", 328069L);
blockheight.put("2015-01-01", 372369L);
blockheight.put("2015-02-01", 416505L);
blockheight.put("2015-03-01", 456631L);
blockheight.put("2015-04-01", 501084L);
blockheight.put("2015-05-01", 543973L);
blockheight.put("2015-06-01", 588326L);
blockheight.put("2015-07-01", 631187L);
blockheight.put("2015-08-01", 675484L);
blockheight.put("2015-09-01", 719725L);
blockheight.put("2015-10-01", 762463L);
blockheight.put("2015-11-01", 806528L);
blockheight.put("2015-12-01", 849041L);
blockheight.put("2016-01-01", 892866L);
blockheight.put("2016-02-01", 936736L);
blockheight.put("2016-03-01", 977691L);
blockheight.put("2016-04-01", 1015848L);
blockheight.put("2016-05-01", 1037417L);
blockheight.put("2016-06-01", 1059651L);
blockheight.put("2016-07-01", 1081269L);
blockheight.put("2016-08-01", 1103630L);
blockheight.put("2016-09-01", 1125983L);
blockheight.put("2016-10-01", 1147617L);
blockheight.put("2016-11-01", 1169779L);
blockheight.put("2016-12-01", 1191402L);
blockheight.put("2017-01-01", 1213861L);
blockheight.put("2017-02-01", 1236197L);
blockheight.put("2017-03-01", 1256358L);
blockheight.put("2017-04-01", 1278622L);
blockheight.put("2017-05-01", 1300239L);
blockheight.put("2017-06-01", 1322564L);
blockheight.put("2017-07-01", 1344225L);
blockheight.put("2017-08-01", 1366664L);
blockheight.put("2017-09-01", 1389113L);
blockheight.put("2017-10-01", 1410738L);
blockheight.put("2017-11-01", 1433039L);
blockheight.put("2017-12-01", 1454639L);
blockheight.put("2018-01-01", 1477201L);
blockheight.put("2018-02-01", 1499599L);
}
public long getHeight(String date) {
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
parser.setLenient(false);
try {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.set(Calendar.DST_OFFSET, 0);
cal.setTime(parser.parse(date));
cal.add(Calendar.DAY_OF_MONTH, -4); // give it some leeway
if (cal.get(Calendar.YEAR) < 2014)
return 1;
if ((cal.get(Calendar.YEAR) == 2014) && (cal.get(Calendar.MONTH) <= 3))
// before May 2014
return 1;
Calendar query = (Calendar) cal.clone();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.set(Calendar.DAY_OF_MONTH, 1);
long prevTime = cal.getTimeInMillis();
String prevDate = formatter.format(prevTime);
// lookup blockheight at first of the month
Long prevBc = blockheight.get(prevDate);
if (prevBc == null) {
// if too recent, go back in time and find latest one we have
while (prevBc == null) {
cal.add(Calendar.MONTH, -1);
if (cal.get(Calendar.YEAR) < 2014) {
throw new IllegalStateException("endless loop looking for blockheight");
}
prevTime = cal.getTimeInMillis();
prevDate = formatter.format(prevTime);
prevBc = blockheight.get(prevDate);
}
}
long height = prevBc;
// now we have a blockheight & a date ON or BEFORE the restore date requested
if (date.equals(prevDate)) return height;
// see if we have a blockheight after this date
cal.add(Calendar.MONTH, 1);
long nextTime = cal.getTimeInMillis();
String nextDate = formatter.format(nextTime);
Long nextBc = blockheight.get(nextDate);
if (nextBc != null) { // we have a range - interpolate the blockheight we are looking for
long diff = nextBc - prevBc;
long diffDays = TimeUnit.DAYS.convert(nextTime - prevTime, TimeUnit.MILLISECONDS);
long days = TimeUnit.DAYS.convert(query.getTimeInMillis() - prevTime,
TimeUnit.MILLISECONDS);
height = Math.round(prevBc + diff * (1.0 * days / diffDays));
} else {
long days = TimeUnit.DAYS.convert(query.getTimeInMillis() - prevTime,
TimeUnit.MILLISECONDS);
height = Math.round(prevBc + 1.0 * days * (24 * 60 / 2));
}
return height;
} catch (ParseException ex) {
throw new IllegalArgumentException(ex);
}
}
}

View File

@@ -89,7 +89,7 @@
android:hint="@string/generate_address_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="center" />
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
@@ -109,7 +109,7 @@
android:hint="@string/generate_viewkey_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="center" />
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
@@ -128,7 +128,7 @@
android:hint="@string/generate_spendkey_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="center" />
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
@@ -144,8 +144,8 @@
android:layout_height="wrap_content"
android:hint="@string/generate_restoreheight_hint"
android:imeOptions="actionDone"
android:inputType="number"
android:textAlignment="center" />
android:inputType="date"
android:textAlignment="textStart" />
</android.support.design.widget.TextInputLayout>
<Button

View File

@@ -10,6 +10,36 @@
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/llXmrToEnabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/moneroBlue"
android:orientation="horizontal">
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_info_white_24dp"
android:gravity="start|center_vertical"
android:padding="8dp"
android:text="@string/info_xmrto_enabled"
android:textColor="@color/white" />
<ImageButton
android:id="@+id/ibXmrToInfoClose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="#00000000"
android:padding="8dp"
android:src="@drawable/ic_close_white_24dp" />
</LinearLayout>
<EditText
android:id="@+id/etDummy"
android:layout_width="0dp"

View File

@@ -6,6 +6,36 @@
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/llXmrToEnabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/moneroBlue"
android:orientation="horizontal">
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_info_white_24dp"
android:gravity="start|center_vertical"
android:padding="8dp"
android:text="@string/info_xmrto_enabled"
android:textColor="@color/white" />
<ImageButton
android:id="@+id/ibXmrToInfoClose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="#00000000"
android:padding="8dp"
android:src="@drawable/ic_close_white_24dp" />
</LinearLayout>
<EditText
android:id="@+id/etDummy"
android:layout_width="0dp"
@@ -16,6 +46,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/llNavBar"
android:layout_below="@id/llXmrToEnabled"
android:padding="8dp" />
<LinearLayout

View File

@@ -16,36 +16,43 @@
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/ivTxType"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center"
android:src="@drawable/ic_xmrto_32dp"
android:visibility="visible"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="9"
android:orientation="vertical">
android:orientation="horizontal">
<TextView
android:id="@+id/tx_amount"
style="@style/MoneroText.PosAmount"
<ImageView
android:id="@+id/ivTxType"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center"
android:src="@drawable/ic_xmrto_32dp"
android:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
tools:text="999.999999" />
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tx_fee"
style="@style/MoneroText.PosFee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
tools:text="Fee 0.06817" />
<TextView
android:id="@+id/tx_amount"
style="@style/MoneroText.PosAmount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
tools:text="+ 999.999999" />
<TextView
android:id="@+id/tx_fee"
style="@style/MoneroText.PosFee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
tools:text="Fee 0.06817" />
</LinearLayout>
</LinearLayout>
<TextView

View File

@@ -18,8 +18,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="1"
android:text="1" />
@@ -29,8 +27,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="2"
android:text="2" />
@@ -40,8 +36,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="3"
android:text="3" />
</LinearLayout>
@@ -50,7 +44,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center">
<TextView
@@ -59,8 +52,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="4"
android:text="4" />
@@ -70,8 +61,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="5"
android:text="5" />
@@ -81,8 +70,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="6"
android:text="6" />
</LinearLayout>
@@ -99,8 +86,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="7"
android:text="7" />
@@ -110,8 +95,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="8"
android:text="8" />
@@ -121,8 +104,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="9"
android:text="9" />
</LinearLayout>
@@ -139,8 +120,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:text="." />
<TextView
@@ -149,8 +128,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:tag="0"
android:text="0" />
@@ -160,7 +137,7 @@
android:layout_height="36sp"
android:layout_gravity="center"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_backspace_black_36dp" />
</LinearLayout>

View File

@@ -172,6 +172,60 @@
comisión de transacción, y lo opuesto en el caso de una prioridad baja. Por favor,
ten en cuenta que si configuras tu transacción con una baja prioridad puede llevar horas
hasta que sea incluída en la cadena de bloques. La prioridad por defecto es \"Media\".</p>
<h1>Enviar BTC</h1>
<h2>XMR.TO</h2>
<p>XMR.TO es un servicio de terceros que actúa como casa de cambio de Monero a Bitcoin.
Usamos la API de XMR.TO para integrar pagos de Bitcoin dentro de Monerujo. Por favor revisa
https://xmr.to y decide por ti mismo si es un servicio que quieres usar. El equipo de Monerujo
no está asociado con XMR.TO y no puede ayudarte con su servicio.</p>
<h2>Tipo de cambio XMR.TO<h2>
<p>En la pantalla de \"Monto\" se te mostrará las condiciones actuales del servicio XMR.TO.
Estas incluyen el tipo de cambio del momento así como también los límites mínimo y máximo de BTC.
Toma nota de que el tipo de cambio aún no está garantizado en esa instancia. También verás el
monto tope hasta el cual la transacción será ejecutada instantáneamente sin esperar a
confirmaciones de XMR (revisa el FAQ de XMR.TO para más detalles). Por favor observa que
XMR.TO no cobra comisiones extra - increíble, verdad?</p>
<h2>Orden XMR.TO<h2>
<p> En la pantalla \"Confirmar\" verás la orden XMR.TO final. Esta orden es válida por un
tiempo limitado - notarás una cuenta atrás en el botón de \"Gastar\". El tipo de cambio a
esta altura puede ser distinto al aproximado que era mostrado en pantallas anteriores.</p>
<h2>Clave secreta XMR.TO<h2>
<p>Dado que Monerujo sólo administra la parte en Monero de tu transacción, puedes usar tu
clave secreta XMR.TO para rastrear la parte en Bitcoin de tu orden en la página de XMR.TO.</p>
<p>Por favor ten en cuenta que esta clave secreta sólo es válida por 24 horas a partir de
iniciada la transacción!</p>
<h2>Cuenta atrás XMR.TO!</h2>
<p>Una vez que la cuenta atrás alcanza el cero, necesitarás pedir una nueva cotización de
parte de XMR.TO, esto se logra dando un paso atrás y luego volviendo a la pantalla de
\"Confirmar\".</p>
]]></string>
<string name="help_xmrto"><![CDATA[
<h1>Enviar BTC</h1>
<h2>XMR.TO</h2>
<p>XMR.TO es un servicio de terceros que actúa como casa de cambio de Monero a Bitcoin.
Usamos la API de XMR.TO para integrar pagos de Bitcoin dentro de Monerujo. Por favor revisa
https://xmr.to y decide por ti mismo si es un servicio que quieres usar. El equipo de Monerujo
no está asociado con XMR.TO y no puede ayudarte con su servicio.</p>
<h2>Tipo de cambio XMR.TO<h2>
<p>En la pantalla de \"Monto\" se te mostrará las condiciones actuales del servicio XMR.TO.
Estas incluyen el tipo de cambio del momento así como también los límites mínimo y máximo de BTC.
Toma nota de que el tipo de cambio aún no está garantizado en esa instancia. También verás el
monto tope hasta el cual la transacción será ejecutada instantáneamente sin esperar a
confirmaciones de XMR (revisa el FAQ de XMR.TO para más detalles). Por favor observa que
XMR.TO no cobra comisiones extra - increíble, verdad?</p>
<h2>Orden XMR.TO<h2>
<p> En la pantalla \"Confirmar\" verás la orden XMR.TO final. Esta orden es válida por un
tiempo limitado - notarás una cuenta atrás en el botón de \"Gastar\". El tipo de cambio a
esta altura puede ser distinto al aproximado que era mostrado en pantallas anteriores.</p>
<h2>Clave secreta XMR.TO<h2>
<p>Dado que Monerujo sólo administra la parte en Monero de tu transacción, puedes usar tu
clave secreta XMR.TO para rastrear la parte en Bitcoin de tu orden en la página de XMR.TO.</p>
<p>Por favor ten en cuenta que esta clave secreta sólo es válida por 24 horas a partir de
iniciada la transacción!</p>
<h2>Cuenta atrás XMR.TO!</h2>
<p>Una vez que la cuenta atrás alcanza el cero, necesitarás pedir una nueva cotización de
parte de XMR.TO, esto se logra dando un paso atrás y luego volviendo a la pantalla de
\"Confirmar\".</p>
]]></string>
</resources>

View File

@@ -13,6 +13,12 @@
<string name="menu_archive">Archivar</string>
<string name="menu_backup">Copia de seguridad</string>
<string name="password_weak">Sigue escribiendo &#8230;</string>
<string name="password_fair">Mas o menos.</string>
<string name="password_good">Puedes hacerlo mejor.</string>
<string name="password_strong">Casi &#8230;</string>
<string name="password_very_strong">¡Bien ahí, hacker nivel 4!</string>
<string name="label_login_wallets">Monederos</string>
<string name="label_donate">Donar</string>
<string name="label_ok">Aceptar</string>
@@ -126,7 +132,7 @@
<string name="generate_title">Crear monedero</string>
<string name="generate_name_hint">Nombre del monedero</string>
<string name="generate_password_hint">Contraseña del monedero</string>
<string name="generate_password_hint">Frase de Contraseña</string>
<string name="generate_buttonGenerate">¡Házme ya un monedero!</string>
<string name="generate_seed">Semilla Mnemotécnica</string>
<string name="generate_button_accept">¡He apuntado estas 25 palabras!</string>
@@ -142,6 +148,8 @@
<string name="generate_wallet_created">Monedero creada</string>
<string name="generate_wallet_create_failed">Creación de monedero fallida</string>
<string name="generate_restoreheight_error">Introduce un número o una fecha (AAAA-MM-DD)</string>
<string name="generate_wallet_type_key">Claves</string>
<string name="generate_wallet_type_new">Nuevo</string>
<string name="generate_wallet_type_seed">Semilla</string>
@@ -151,7 +159,7 @@
<string name="generate_viewkey_hint">Clave de Vista</string>
<string name="generate_spendkey_hint">Clave de Gasto</string>
<string name="generate_mnemonic_hint">Semilla Mnemotécnica de 25 Palabras</string>
<string name="generate_restoreheight_hint">Altura de Restauración</string>
<string name="generate_restoreheight_hint">Altura o Fecha (YYYY-MM-DD) de Restauración</string>
<string name="generate_wallet_label">Monedero</string>
<string name="generate_password_label">Contraseña</string>
@@ -165,7 +173,7 @@
<string name="generate_check_address">Introduce una dirección válida</string>
<string name="generate_check_mnemonic">Introduce tu semilla de 25 palabras</string>
<string name="send_address_hint">Dirección del Destinatario</string>
<string name="send_address_hint">Dirección XMR o BTC del Destinatario</string>
<string name="send_paymentid_hint">ID de Pago (opcional)</string>
<string name="send_amount_hint">0.00</string>
<string name="send_notes_hint">Notas Privadas (opcional)</string>
@@ -260,7 +268,7 @@
<item>Prioridad Alta</item>
</string-array>
<string name="fab_create_new">Crear nueva monedero</string>
<string name="fab_create_new">Crear nuevo monedero</string>
<string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string>
<string name="fab_restore_key">Restaurar monedero con claves privadas</string>
<string name="fab_restore_seed">Restaurar monedero con semilla de 25 palabras</string>
@@ -328,4 +336,5 @@
<string name="receive_bitcoin_paymentid_invalid">Debe quedar vacío con una dirección Bitcoin</string>
<string name="about_whoami">Soy monerujo</string>
<string name="info_send_xmrto_success_order_label">Orden XMR.TO</string>
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
</resources>

View File

@@ -2,8 +2,8 @@
<resources>
<color name="tx_green">@color/take</color>
<color name="tx_red">@color/give</color>
<color name="tx_pending">@color/gradientPink</color>
<color name="tx_failed">@color/moneroFab</color>
<color name="tx_pending">@color/moneroFab</color>
<color name="tx_failed">@color/moneroBlack</color>
<color name="gradientOrange">#FFFF6105</color>
<color name="gradientPink">#FFF0006B</color>
@@ -18,6 +18,7 @@
<color name="moneroOrange">#cc5100</color>
<color name="moneroWhite">#ffffff</color>
<color name="moneroBlack">#000000</color>
<color name="moneroBlue">#1F4E97</color>
<color name="moneroGray">#FD9B9B9B</color>
<color name="dotGray">#FF4A4A4A</color>

View File

@@ -178,8 +178,39 @@
<h2>XMR.TO Secret Key<h2>
<p>Since Monerujo only handles the Monero part of your transaction your XMR.TO secret key
can be used to track the Bitcoin part of your order on the XMR.TO homepage.</p>
<p>Please note, that this secret key is only valid for 24 hours after the transaction is
started!</p>
<h2>XMR.TO Countdown!</h2>
<p>Once the countdown reaches zero, you need to get a new quote from XMR.TO by going back to the
previous step and then coming back to the \"Confirm\" screen.</p>
]]></string>
<string name="help_xmrto"><![CDATA[
<h1>Sending BTC</h1>
<h2>XMR.TO</h2>
<p>XMR.TO is a third party service which acts as an exchange from Monero to Bitcoin.
We use the XMR.TO API to integrate Bitcoin payments into Monerujo. Please check out
https://xmr.to and decide for yourself if this is something you want to use. The Monerujo
Team is not associated with XMR.TO and cannot help you with their service.</p>
<h2>XMR.TO Exchange Rate<h2>
<p>On the \"Amount\" screen you will be shown the current parameters of the XMR.TO service. These
include the current exchange rate as well as upper and lower BTC limits. Note that this
rate is not guaranteed at this point. You will also see
the amount up to which the BTC transaction will be executed instantly without waiting for
XMR confirmations (see the XMR.TO FAQ for more details). Please note, that XMR.TO does
not charge extra fees - how cool is that?</p>
<h2>XMR.TO Order<h2>
<p>On the \"Confirm\" screen, you will see the actual XMR.TO order. This order is valid for
a limited time - you may notice a countdown on the \"Spend\" button. The exchange rate may
be different to the indicative one shown on previous screens.</p>
<h2>XMR.TO Secret Key<h2>
<p>Since Monerujo only handles the Monero part of your transaction your XMR.TO secret key
can be used to track the Bitcoin part of your order on the XMR.TO homepage.</p>
<p>Please note, that this secret key is only valid for 24 hours after the transaction is
started!</p>
<h2>XMR.TO Countdown!</h2>
<p>Once the countdown reaches zero, you need to get a new quote from XMR.TO by going back to the
previous step and then coming back to the \"Confirm\" screen.</p>
]]></string>
</resources>

View File

@@ -15,6 +15,12 @@
<string name="menu_archive">Archive</string>
<string name="menu_backup">Backup</string>
<string name="password_weak">Continue typing &#8230;</string>
<string name="password_fair">Meh &#8230;</string>
<string name="password_good">C\'mon, you can do better!</string>
<string name="password_strong">Getting there &#8230;</string>
<string name="password_very_strong">Yeah baby, h4x0r style!</string>
<string name="label_login_wallets">Wallets</string>
<string name="label_donate">Donate</string>
<string name="label_ok">OK</string>
@@ -28,6 +34,8 @@
<string name="label_receive_info_gen_qr_code">Touch for QR Code</string>
<string name="info_send_prio_fees">Higher Priority = Higher Fees</string>
<string name="info_xmrto_enabled">BTC payment enabled, tap for more info.</string>
<string name="info_xmrto"><![CDATA[
<b>You entered a Bitcoin address.</b><br/>
<i>You&apos;ll send XMR and the receiver will get BTC using the <b>XMR.TO</b> service.</i>
@@ -195,7 +203,7 @@
<string name="generate_title">Create Wallet</string>
<string name="generate_name_hint">Wallet Name</string>
<string name="generate_password_hint">Wallet Password</string>
<string name="generate_password_hint">Wallet Passphrase</string>
<string name="generate_buttonGenerate">Make me a wallet already!</string>
<string name="generate_seed">Mnemonic Seed</string>
<string name="generate_button_accept">I have noted these 25 words!</string>
@@ -211,6 +219,8 @@
<string name="generate_wallet_created">Wallet created</string>
<string name="generate_wallet_create_failed">Wallet create failed</string>
<string name="generate_restoreheight_error">Enter Number or Date (YYYY-MM-DD)</string>
<string name="generate_wallet_type_key">Keys</string>
<string name="generate_wallet_type_new">New</string>
<string name="generate_wallet_type_seed">Seed</string>
@@ -220,7 +230,7 @@
<string name="generate_viewkey_hint">View Key</string>
<string name="generate_spendkey_hint">Spend Key</string>
<string name="generate_mnemonic_hint">25-Word Mnemonic Seed</string>
<string name="generate_restoreheight_hint">Restore Height</string>
<string name="generate_restoreheight_hint">Restore Height or Date (YYYY-MM-DD)</string>
<string name="generate_wallet_label">Wallet</string>
<string name="generate_password_label">Password</string>
@@ -236,7 +246,7 @@
<string name="send_amount_btc_xmr">%1$s (indicative)</string>
<string name="send_address_hint">Receiver\'s Address</string>
<string name="send_address_hint">Receiver\'s XMR or BTC Address</string>
<string name="send_paymentid_hint">Payment ID (optional)</string>
<string name="send_amount_hint">0.00</string>
<string name="send_notes_hint">Private Notes (optional)</string>

View File

@@ -119,6 +119,8 @@
<style name="MoneroLabel.NumPad">
<item name="android:textSize">36sp</item>
<item name="android:textColor">@color/moneroBlack</item>
<item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
<item name="android:gravity">center</item>
</style>
<style name="MoneroLabel.Title">

View File

@@ -25,6 +25,7 @@ public class BitcoinAddressValidatorTest {
@Test
public void validateBTC_shouldValidate() {
assertTrue(BitcoinAddressValidator.validate("2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy", true));
assertTrue(BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false));
assertTrue(BitcoinAddressValidator.validate("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", false));
assertTrue(BitcoinAddressValidator.validate("3R2MPpTNQLCNs13qnHz89Rm82jQ27bAwft", false));

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