mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-08 20:40:51 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b1f530e64a | ||
![]() |
8c086b77d3 | ||
![]() |
c0fdfe87be | ||
![]() |
6cfd840283 | ||
![]() |
d80cde1136 | ||
![]() |
bea4b06675 | ||
![]() |
88bc33b5a4 | ||
![]() |
2db36bb824 | ||
![]() |
dd689b1883 | ||
![]() |
641abd13f5 | ||
![]() |
a0b3a7fe5d | ||
![]() |
a0486f581f |
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 71
|
||||
versionName "1.3.11 '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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,28 @@ android {
|
||||
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 {
|
||||
@@ -50,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"
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -144,7 +144,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generate_restoreheight_hint"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:inputType="date"
|
||||
android:textAlignment="textStart" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
||||
@@ -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>
|
||||
|
@@ -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>
|
||||
@@ -197,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>
|
||||
@@ -213,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>
|
||||
@@ -222,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>
|
||||
|
@@ -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));
|
||||
|
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
// all ranges go back 5 days
|
||||
|
||||
public class RestoreHeightTest {
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void pre2014() {
|
||||
assertTrue(getHeight("2013-12-01") == 1);
|
||||
assertTrue(getHeight("1958-12-01") == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zero() {
|
||||
assertTrue(getHeight("2014-04-27") == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notZero() {
|
||||
assertTrue(getHeight("2014-05-07") > 1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateA() {
|
||||
getHeight("2013-13-04");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateB() {
|
||||
getHeight("2013-13-01-");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateC() {
|
||||
getHeight("x013-13-01");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateD() {
|
||||
getHeight("2013-12-41");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test201709() {
|
||||
// getHeight() returns blockheight of < two days ago
|
||||
assertTrue(isInRange(getHeight("2017-09-01"), 1383957, 1387716));
|
||||
assertTrue(isInRange(getHeight("2017-09-05"), 1386967, 1390583));
|
||||
assertTrue(isInRange(getHeight("2017-09-21"), 1398492, 1402068));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test20160324() { // blocktime changed from 1 minute to 2 minutes on this day
|
||||
assertTrue(isInRange(getHeight("2016-03-23"), 998955, 1006105));
|
||||
assertTrue(isInRange(getHeight("2016-03-24"), 1000414, 1007486));
|
||||
assertTrue(isInRange(getHeight("2016-03-25"), 1001800, 1008900));
|
||||
assertTrue(isInRange(getHeight("2016-03-26"), 1003243, 1009985));
|
||||
assertTrue(isInRange(getHeight("2016-03-27"), 1004694, 1010746));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2014() {
|
||||
assertTrue(isInRange(getHeight("2014-04-26"), 1, 8501));
|
||||
assertTrue(isInRange(getHeight("2014-05-09"), 20289, 28311));
|
||||
assertTrue(isInRange(getHeight("2014-05-17"), 32608, 40075));
|
||||
assertTrue(isInRange(getHeight("2014-05-30"), 52139, 59548));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2015() {
|
||||
assertTrue(isInRange(getHeight("2015-01-26"), 397914, 405055));
|
||||
assertTrue(isInRange(getHeight("2015-08-13"), 682595, 689748));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2016() {
|
||||
assertTrue(isInRange(getHeight("2016-01-26"), 918313, 925424));
|
||||
assertTrue(isInRange(getHeight("2016-08-13"), 1107244, 1110793));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2017() {
|
||||
assertTrue(isInRange(getHeight("2017-01-26"), 1226806, 1230402));
|
||||
assertTrue(isInRange(getHeight("2017-08-13"), 1370264, 1373854));
|
||||
assertTrue(isInRange(getHeight("2017-08-31"), 1383254, 1386967));
|
||||
assertTrue(isInRange(getHeight("2017-06-09"), 1323288, 1326884));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post201802() {
|
||||
assertTrue(isInRange(getHeight("2018-02-19"), 1507579, 1511127));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFuture() {
|
||||
long b_20180208 = 1504715;
|
||||
long b_20180808 = b_20180208 + 720 * (28 + 31 + 30 + 31 + 30 + 31);
|
||||
assertTrue(isInRange(getHeight("2018-08-08"), b_20180808 - 720 * 5, b_20180808));
|
||||
}
|
||||
|
||||
|
||||
private boolean isInRange(long n, long min, long max) {
|
||||
if (n > max) return false;
|
||||
if (n < min) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private long getHeight(String date) {
|
||||
return RestoreHeight.getInstance().getHeight(date);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
BIN
external-libs/boost/lib/arm64-v8a/libboost_chrono.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_chrono.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_date_time.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_date_time.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_filesystem.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_filesystem.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_program_options.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_program_options.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_regex.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_regex.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_serialization.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_serialization.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_system.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_system.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_thread.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_thread.a
Normal file
Binary file not shown.
BIN
external-libs/boost/lib/arm64-v8a/libboost_wserialization.a
Normal file
BIN
external-libs/boost/lib/arm64-v8a/libboost_wserialization.a
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user