mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-08 20:40:51 +02:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b1f530e64a | ||
![]() |
8c086b77d3 | ||
![]() |
c0fdfe87be | ||
![]() |
6cfd840283 | ||
![]() |
d80cde1136 | ||
![]() |
bea4b06675 | ||
![]() |
88bc33b5a4 | ||
![]() |
2db36bb824 | ||
![]() |
dd689b1883 | ||
![]() |
641abd13f5 | ||
![]() |
a0b3a7fe5d | ||
![]() |
a0486f581f | ||
![]() |
36161137ec | ||
![]() |
c9927edbd1 | ||
![]() |
f0a3c05a9a | ||
![]() |
d6eb82c457 | ||
![]() |
7f47307307 | ||
![]() |
165dd3dfd1 | ||
![]() |
171727c9db | ||
![]() |
1492814ec9 | ||
![]() |
9b1225fe4b | ||
![]() |
595d88e42e | ||
![]() |
0e207d7401 | ||
![]() |
5920d6c9e8 |
27
.circleci/config.yml
Normal file
27
.circleci/config.yml
Normal 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
|
@@ -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"
|
||||
|
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher.png
Executable file
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher.png
Executable file
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher.png
Executable file
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
Executable file
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
Executable file
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
4
app/src/debug/res/values/strings.xml
Normal file
4
app/src/debug/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">monerujo - Debug</string>
|
||||
</resources>
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
152
app/src/main/java/com/m2049r/xmrwallet/util/RestoreHeight.java
Normal file
152
app/src/main/java/com/m2049r/xmrwallet/util/RestoreHeight.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -13,6 +13,12 @@
|
||||
<string name="menu_archive">Archivar</string>
|
||||
<string name="menu_backup">Copia de seguridad</string>
|
||||
|
||||
<string name="password_weak">Sigue escribiendo …</string>
|
||||
<string name="password_fair">Mas o menos.</string>
|
||||
<string name="password_good">Puedes hacerlo mejor.</string>
|
||||
<string name="password_strong">Casi …</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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -15,6 +15,12 @@
|
||||
<string name="menu_archive">Archive</string>
|
||||
<string name="menu_backup">Backup</string>
|
||||
|
||||
<string name="password_weak">Continue typing …</string>
|
||||
<string name="password_fair">Meh …</string>
|
||||
<string name="password_good">C\'mon, you can do better!</string>
|
||||
<string name="password_strong">Getting there …</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'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>
|
||||
|
@@ -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">
|
||||
|
@@ -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
Reference in New Issue
Block a user