1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-04 00:53:36 +02:00

Compare commits

...

40 Commits

Author SHA1 Message Date
m2049r
849718fdc7 bump version 2021-03-15 09:38:01 +01:00
m2049r
a6372f701d Merge pull request #730 from m2049r/feature_sr_transitions_s
Send & Receive UI transitions
2021-03-14 21:24:18 +01:00
m2049r
12f135bb14 refactoring 2021-03-14 21:22:49 +01:00
m2049r
16870fcbb9 Receive & send transition 2021-03-14 21:22:37 +01:00
m2049r
2fbd152fb3 bump version 2021-03-13 13:16:26 +01:00
m2049r
a7b178e024 Tweaking UI & some bugfixing on the way (#729) 2021-03-13 13:12:46 +01:00
m2049r
c5a035437b bump version 2021-03-12 20:16:05 +01:00
m2049r
75c550fd19 add copyright notice 2021-03-12 20:02:30 +01:00
m2049r
1b680344b1 call fail() if password cancelled (#726) 2021-03-12 19:57:13 +01:00
m2049r
3329636d32 adjust areContentsTheSame criteria 2021-03-12 19:56:42 +01:00
m2049r
77e9bf7c43 fix Node equality 2021-03-12 19:56:42 +01:00
m2049r
40f5c9365e Merge pull request #725 from m2049r/fix_recyclerdiff
Fix transactions not updating when switching accounts
2021-03-12 01:00:21 +01:00
m2049r
850ba7efef clean whitespace & style 2021-03-12 00:57:22 +01:00
m2049r
548369ca0b fix updating when transfers between accounts 2021-03-12 00:49:12 +01:00
yorha-0x
afc0d5b7bc Update recyclerviews with diffutil (#711)
Remove modified imports

Co-authored-by: m2049r <m2049r@monerujo.io>
2021-03-11 20:57:19 +01:00
m2049r
02e92db59a bump version 2021-03-10 19:41:39 +01:00
m2049r
2b34454214 Fix restoreheights prior to 2014 (#723)
* fix pre-2014 dates
* update heights
2021-03-10 19:29:10 +01:00
Lafudoci
12706119d3 Update zh-rTW translation (#719) 2021-03-10 19:27:49 +01:00
m2049r
f7a762f32b show passphrase strength with icons (#722) 2021-03-10 19:13:30 +01:00
m2049r
86d5d2a2cb Update BUILDING-external-libs.md 2021-02-21 23:01:48 +01:00
m2049r
e56369b91a fix tests (#717) 2021-02-19 17:25:11 +01:00
m2049r
b04aa24269 bump version 2021-02-19 16:35:02 +01:00
m2049r
dda86bd5de fix URI generation (#716) 2021-02-19 16:32:15 +01:00
m2049r
a19ad7fd52 change build to docker (#715) 2021-02-19 16:31:35 +01:00
m2049r
0443fd808c bump version (consolidating with play release) 2021-02-17 00:16:24 +01:00
m2049r
faf57c96fc add more output currencies (#714) 2021-02-15 21:38:26 +01:00
m2049r
57ddddfce2 replace xmr.to with SideShift.ai (#710)
replace xmr.to with SideShift.ai
random bugfixes
upgrade gradle & dependencies
2021-02-13 00:01:19 +01:00
netrik182
ab6069058b new pt-br strings after review (#708) 2021-02-01 23:02:36 +01:00
m2049r
d94d6e6925 downgrade to APK 29 & bump version 2020-11-22 19:39:30 +01:00
m2049r
4a819cc159 bump version 2020-11-21 22:18:36 +01:00
m2049r
f40c3d6c6d catch cases where the qr code produces a null monero address (#702) 2020-11-21 22:08:23 +01:00
m2049r
82b25df7ad better node selection (#701) 2020-11-21 21:54:21 +01:00
m2049r
08f815e830 don't show connection error if we havent tested yet (#700) 2020-11-21 13:22:54 +01:00
m2049r
4e59be2dff New handling of nodes (#699)
* rework node handling

* update block heights
2020-11-19 16:36:01 +01:00
m2049r
45c5883e11 fix potential NPE in updateAccountsList() 2020-10-20 23:47:41 +02:00
m2049r
067a23e6a5 fix crash if back before new address is generated 2020-10-20 23:40:55 +02:00
m2049r
d21fd41c44 update dependencies & fix MethodNotFound exception 2020-10-20 23:31:29 +02:00
m2049r
6632547d1e bump version & fix versionCode (#694) 2020-10-19 23:44:40 +02:00
m2049r
f1b786ec3e fix keyboard crash when no view in focus (#693) 2020-10-19 23:42:22 +02:00
m2049r
a93e96d34c upgrade to monero v0.17.1.1 (#692) 2020-10-19 23:39:30 +02:00
247 changed files with 7250 additions and 6452 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@
/app/alphaStagenet
/app/prodStagenet
/app/.cxx
/monerujo.id

View File

@@ -13,7 +13,7 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs)
add_library(sodium STATIC IMPORTED)
set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/libsodium/lib/${ANDROID_ABI}/libsodium.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libsodium.a)
############
# OpenSSL
@@ -21,11 +21,11 @@ set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libcrypto.a)
add_library(ssl STATIC IMPORTED)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libssl.a)
############
# Boost
@@ -33,39 +33,39 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
add_library(boost_chrono STATIC IMPORTED)
set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_chrono.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_chrono.a)
add_library(boost_date_time STATIC IMPORTED)
set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_date_time.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_date_time.a)
add_library(boost_filesystem STATIC IMPORTED)
set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_filesystem.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_filesystem.a)
add_library(boost_program_options STATIC IMPORTED)
set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_program_options.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_program_options.a)
add_library(boost_regex STATIC IMPORTED)
set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_regex.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_regex.a)
add_library(boost_serialization STATIC IMPORTED)
set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_serialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_serialization.a)
add_library(boost_system STATIC IMPORTED)
set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_system.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_system.a)
add_library(boost_thread STATIC IMPORTED)
set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_thread.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_thread.a)
add_library(boost_wserialization STATIC IMPORTED)
set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_wserialization.a)
#############
# Monero
@@ -73,99 +73,103 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet_api.a)
add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet.a)
add_library(cryptonote_core STATIC IMPORTED)
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_core.a)
add_library(cryptonote_basic STATIC IMPORTED)
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_basic.a)
add_library(mnemonics STATIC IMPORTED)
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmnemonics.a)
add_library(common STATIC IMPORTED)
set_target_properties(common PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcommon.a)
add_library(cncrypto STATIC IMPORTED)
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcncrypto.a)
add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct.a)
add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct_basic.a)
add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblockchain_db.a)
add_library(lmdb STATIC IMPORTED)
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/liblmdb.a)
add_library(easylogging STATIC IMPORTED)
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libeasylogging.a)
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libunbound.a)
add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libepee.a)
add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblocks.a)
add_library(checkpoints STATIC IMPORTED)
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice.a)
add_library(device_trezor STATIC IMPORTED)
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice_trezor.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libversion.a)
add_library(net STATIC IMPORTED)
set_target_properties(net PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libnet.a)
add_library(hardforks STATIC IMPORTED)
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libhardforks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libhardforks.a)
add_library(randomx STATIC IMPORTED)
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librandomx.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librandomx.a)
add_library(rpc_base STATIC IMPORTED)
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librpc_base.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librpc_base.a)
add_library(wallet-crypto STATIC IMPORTED)
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a)
#############
# System
@@ -173,10 +177,16 @@ set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
find_library( log-lib log )
include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
include_directories( ${EXTERNAL_LIBS_DIR}/include )
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
if(${ANDROID_ABI} STREQUAL "x86_64")
set(EXTRA_LIBS "wallet-crypto")
else()
set(EXTRA_LIBS "")
endif()
target_link_libraries( monerujo
wallet_api
@@ -203,6 +213,7 @@ target_link_libraries( monerujo
randomx
hardforks
rpc_base
${EXTRA_LIBS}
boost_chrono
boost_date_time

View File

@@ -1,14 +1,14 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion '29.0.2'
compileSdkVersion 29
buildToolsVersion '29.0.3'
defaultConfig {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 28
versionCode 502
versionName "1.15.2 'Dark Fork'"
targetSdkVersion 29
versionCode 712
versionName "1.17.12 'Druk'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -56,6 +56,9 @@ android {
debug {
applicationIdSuffix ".debug"
}
applicationVariants.all { variant ->
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
}
}
externalNativeBuild {
@@ -92,8 +95,7 @@ android {
variant.outputs.all {
output ->
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
//def flavor = output.getFilter(flavor)
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * versionCode
if (abiName == null) abiName = "universal"
def v = "${variant.versionName}".replaceFirst(" '.*' ?", "")
@@ -110,20 +112,26 @@ android {
}
}
def getId(name) {
def Properties props = new Properties()
props.load(new FileInputStream(new File('monerujo.id')))
return props[name]
}
dependencies {
implementation 'androidx.core:core:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation group: 'com.google.android.material', name: 'material', version: '1.3.0-alpha03'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
implementation "com.burgstaller:okhttp-digest:2.0"
implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "com.burgstaller:okhttp-digest:2.1"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation 'com.nulab-inc:zxcvbn:1.3.0'
@@ -132,12 +140,14 @@ dependencies {
implementation 'org.slf4j:slf4j-nop:1.7.30'
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
// https://mvnrepository.com/artifact/com.github.aelstad/keccakj
implementation 'com.github.aelstad:keccakj:1.1.0'
testImplementation "junit:junit:$rootProject.ext.junitVersion"
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
testImplementation 'org.json:json:20180813'
testImplementation 'net.jodah:concurrentunit:0.4.4'
compileOnly 'org.projectlombok:lombok:1.18.16'
annotationProcessor 'org.projectlombok:lombok:1.18.16'
}

View File

@@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:requestLegacyExternalStorage="true"
android:name=".XmrWalletApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"

View File

@@ -185,7 +185,6 @@ public class Dispatcher implements PeerRetriever.OnGetPeers {
public void seedPeers(Collection<NodeInfo> seedNodes) {
for (NodeInfo node : seedNodes) {
node.clear();
if (node.isFavourite()) {
rpcNodes.add(node);
if (listener != null) listener.onGet(node);

View File

@@ -1,3 +1,19 @@
/*
* 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.levin.scanner;
import java.net.InetAddress;

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017-2020 m2049r et al.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.app.PendingIntent;
@@ -197,7 +213,7 @@ public class BaseActivity extends SecureActivity implements GenerateReviewFragme
if (uri == null) {
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
} else {
BarcodeData bc = BarcodeData.fromQrCode(uri.toString());
BarcodeData bc = BarcodeData.fromString(uri.toString());
if (bc == null)
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
else

View File

@@ -50,9 +50,8 @@ import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.util.ledger.Monero;
import com.m2049r.xmrwallet.widget.PasswordEntryView;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.Zxcvbn;
import java.io.File;
import java.text.ParseException;
@@ -70,7 +69,7 @@ public class GenerateFragment extends Fragment {
static final String TYPE_VIEWONLY = "view";
private TextInputLayout etWalletName;
private TextInputLayout etWalletPassword;
private PasswordEntryView etWalletPassword;
private LinearLayout llFingerprintAuth;
private TextInputLayout etWalletAddress;
private TextInputLayout etWalletMnemonic;
@@ -131,21 +130,6 @@ public class GenerateFragment extends Fragment {
});
clearErrorOnTextEntry(etWalletName);
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) {
}
});
etWalletMnemonic.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
@@ -354,45 +338,10 @@ public class GenerateFragment extends Fragment {
});
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;
@@ -429,7 +378,7 @@ public class GenerateFragment extends Fragment {
}
private long getHeight() {
long height = 0;
long height = -1;
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
if (restoreHeight.isEmpty()) return -1;
@@ -440,7 +389,7 @@ public class GenerateFragment extends Fragment {
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ex) {
}
if ((height <= 0) && (restoreHeight.length() == 8))
if ((height < 0) && (restoreHeight.length() == 8))
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
@@ -448,7 +397,7 @@ public class GenerateFragment extends Fragment {
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ex) {
}
if (height <= 0)
if (height < 0)
try {
// or is it a height?
height = Long.parseLong(restoreHeight);
@@ -654,24 +603,7 @@ public class GenerateFragment extends Fragment {
final TextInputLayout etSeed = promptsView.findViewById(R.id.etSeed);
final TextInputLayout etPassphrase = promptsView.findViewById(R.id.etPassphrase);
etSeed.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (etSeed.getError() != null) {
etSeed.setError(null);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
}
});
clearErrorOnTextEntry(etSeed);
alertDialogBuilder
.setCancelable(false)

View File

@@ -56,6 +56,7 @@ import com.m2049r.xmrwallet.util.FingerprintHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.widget.PasswordEntryView;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
@@ -473,7 +474,7 @@ public class GenerateReviewFragment extends Fragment {
AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(getActivity());
alertDialogBuilder.setView(promptsView);
final TextInputLayout etPasswordA = promptsView.findViewById(R.id.etWalletPasswordA);
final PasswordEntryView etPasswordA = promptsView.findViewById(R.id.etWalletPasswordA);
etPasswordA.setHint(getString(R.string.prompt_changepw, walletName));
final TextInputLayout etPasswordB = promptsView.findViewById(R.id.etWalletPasswordB);
@@ -509,9 +510,6 @@ public class GenerateReviewFragment extends Fragment {
etPasswordA.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (etPasswordA.getError() != null) {
etPasswordA.setError(null);
}
if (etPasswordB.getError() != null) {
etPasswordB.setError(null);
}
@@ -531,9 +529,6 @@ public class GenerateReviewFragment extends Fragment {
etPasswordB.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (etPasswordA.getError() != null) {
etPasswordA.setError(null);
}
if (etPasswordB.getError() != null) {
etPasswordB.setError(null);
}
@@ -564,29 +559,20 @@ public class GenerateReviewFragment extends Fragment {
});
openDialog = alertDialogBuilder.create();
openDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(final DialogInterface dialog) {
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String newPasswordA = etPasswordA.getEditText().getText().toString();
String newPasswordB = etPasswordB.getEditText().getText().toString();
// disallow empty passwords
if (newPasswordA.isEmpty()) {
etPasswordA.setError(getString(R.string.generate_empty_passwordB));
} else if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else {
new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
Helper.hideKeyboardAlways(getActivity());
openDialog.dismiss();
openDialog = null;
}
}
});
}
openDialog.setOnShowListener(dialog -> {
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view -> {
String newPasswordA = etPasswordA.getEditText().getText().toString();
String newPasswordB = etPasswordB.getEditText().getText().toString();
if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else {
new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
Helper.hideKeyboardAlways(getActivity());
openDialog.dismiss();
openDialog = null;
}
});
});
// accept keyboard "ok"
@@ -597,9 +583,7 @@ public class GenerateReviewFragment extends Fragment {
String newPasswordA = etPasswordA.getEditText().getText().toString();
String newPasswordB = etPasswordB.getEditText().getText().toString();
// disallow empty passwords
if (newPasswordA.isEmpty()) {
etPasswordA.setError(getString(R.string.generate_empty_passwordB));
} else if (!newPasswordA.equals(newPasswordB)) {
if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else {
new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,7 @@ import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.NodePinger;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.widget.Toolbar;
@@ -105,6 +106,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
Set<NodeInfo> getFavouriteNodes();
Set<NodeInfo> getOrPopulateFavourites();
boolean hasLedger();
}
@@ -132,11 +135,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
activityCallback.setTitle(null);
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
activityCallback.showNet();
NodeInfo node = activityCallback.getNode();
if (node == null)
findBestNode();
else
showNode(node);
pingSelectedNode();
}
@Override
@@ -186,23 +185,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
pbNode = view.findViewById(R.id.pbNode);
llNode = view.findViewById(R.id.llNode);
llNode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (activityCallback.getFavouriteNodes().isEmpty())
startNodePrefs();
else
findBestNode();
}
});
llNode.setOnClickListener(v -> startNodePrefs());
tvNodeName = view.findViewById(R.id.tvNodeName);
tvNodeAddress = view.findViewById(R.id.tvNodeAddress);
view.findViewById(R.id.ibOption).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startNodePrefs();
}
});
view.findViewById(R.id.ibRenew).setOnClickListener(v -> findBestNode());
Helper.hideKeyboard(getActivity());
@@ -272,7 +258,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
walletList.addAll(walletInfos);
filterList();
adapter.setInfos(displayedList);
adapter.notifyDataSetChanged();
// deal with Gunther & FAB animation
if (displayedList.isEmpty()) {
@@ -421,32 +406,57 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
public void findBestNode() {
new AsyncFindBestNode().execute();
new AsyncFindBestNode().execute(AsyncFindBestNode.FIND_BEST);
}
private class AsyncFindBestNode extends AsyncTask<Void, Void, NodeInfo> {
public void pingSelectedNode() {
new AsyncFindBestNode().execute(AsyncFindBestNode.PING_SELECTED);
}
private NodeInfo autoselect(Set<NodeInfo> nodes) {
if (nodes.isEmpty()) return null;
NodePinger.execute(nodes, null);
List<NodeInfo> nodeList = new ArrayList<>(nodes);
Collections.sort(nodeList, NodeInfo.BestNodeComparator);
return nodeList.get(0);
}
private class AsyncFindBestNode extends AsyncTask<Integer, Void, NodeInfo> {
final static int PING_SELECTED = 0;
final static int FIND_BEST = 1;
@Override
protected void onPreExecute() {
super.onPreExecute();
pbNode.setVisibility(View.VISIBLE);
llNode.setVisibility(View.INVISIBLE);
activityCallback.setNode(null);
}
@Override
protected NodeInfo doInBackground(Void... params) {
List<NodeInfo> nodesToTest = new ArrayList<>(activityCallback.getFavouriteNodes());
Timber.d("testing best node from %d", nodesToTest.size());
if (nodesToTest.isEmpty()) return null;
for (NodeInfo node : nodesToTest) {
node.testRpcService(); // TODO: do this in parallel?
// no: it's better if it looks like it's doing something
}
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
NodeInfo bestNode = nodesToTest.get(0);
if (bestNode.isValid()) {
activityCallback.setNode(bestNode);
return bestNode;
protected NodeInfo doInBackground(Integer... params) {
Set<NodeInfo> favourites = activityCallback.getOrPopulateFavourites();
NodeInfo selectedNode;
if (params[0] == FIND_BEST) {
selectedNode = autoselect(favourites);
} else if (params[0] == PING_SELECTED) {
selectedNode = activityCallback.getNode();
if (!activityCallback.getFavouriteNodes().contains(selectedNode))
selectedNode = null; // it's not in the favourites (any longer)
if (selectedNode == null)
for (NodeInfo node : favourites) {
if (node.isSelected()) {
selectedNode = node;
break;
}
}
if (selectedNode == null) { // autoselect
selectedNode = autoselect(favourites);
} else
selectedNode.testRpcService();
} else throw new IllegalStateException();
if ((selectedNode != null) && selectedNode.isValid()) {
activityCallback.setNode(selectedNode);
return selectedNode;
} else {
activityCallback.setNode(null);
return null;
@@ -462,17 +472,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
Timber.d("found a good node %s", result.toString());
showNode(result);
} else {
if (!activityCallback.getFavouriteNodes().isEmpty()) {
tvNodeName.setText(getResources().getText(R.string.node_refresh_hint));
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_refresh_black_24dp, 0, 0, 0);
tvNodeAddress.setText(null);
tvNodeAddress.setVisibility(View.GONE);
} else {
tvNodeName.setText(getResources().getText(R.string.node_create_hint));
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
tvNodeAddress.setText(null);
tvNodeAddress.setVisibility(View.GONE);
}
tvNodeName.setText(getResources().getText(R.string.node_create_hint));
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
tvNodeAddress.setText(null);
tvNodeAddress.setVisibility(View.GONE);
}
}
@@ -485,12 +488,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private void showNode(NodeInfo nodeInfo) {
tvNodeName.setText(nodeInfo.getName());
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(NodeInfoAdapter.getPingIcon(nodeInfo), 0, 0, 0);
tvNodeAddress.setText(nodeInfo.getAddress());
Helper.showTimeDifference(tvNodeAddress, nodeInfo.getTimestamp());
tvNodeAddress.setVisibility(View.VISIBLE);
}
private void startNodePrefs() {
activityCallback.setNode(null);
activityCallback.onNodePrefs();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -73,14 +73,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
// * I don't know why this is the case but I don't have the time to figure out.
Handler handler = new Handler();
handler.postDelayed(new
Runnable() {
@Override
public void run() {
mScannerView.resumeCameraPreview(ScannerFragment.this);
}
}, 2000);
handler.postDelayed(() -> mScannerView.resumeCameraPreview(ScannerFragment.this), 2000);
}
@Override

View File

@@ -16,8 +16,11 @@
package com.m2049r.xmrwallet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.view.LayoutInflater;
@@ -25,6 +28,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -32,10 +36,12 @@ import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.widget.Toolbar;
@@ -77,6 +83,9 @@ public class TxFragment extends Fragment {
private TextView tvTxXmrToKey;
private TextView tvDestinationBtc;
private TextView tvTxAmountBtc;
private TextView tvXmrToSupport;
private TextView tvXmrToKeyLabel;
private ImageView tvXmrToLogo;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -88,6 +97,9 @@ public class TxFragment extends Fragment {
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
tvAccount = view.findViewById(R.id.tvAccount);
tvAddress = view.findViewById(R.id.tvAddress);
@@ -104,12 +116,9 @@ public class TxFragment extends Fragment {
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
}
tvTxXmrToKey.setOnClickListener(v -> {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
});
Bundle args = getArguments();
@@ -283,12 +292,36 @@ public class TxFragment extends Fragment {
showBtcInfo();
}
@SuppressLint("SetTextI18n")
void showBtcInfo() {
if (userNotes.xmrtoKey != null) {
cvXmrTo.setVisibility(View.VISIBLE);
tvTxXmrToKey.setText(userNotes.xmrtoKey);
String key = userNotes.xmrtoKey;
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
key = "xmrto-" + key;
}
tvTxXmrToKey.setText(key);
tvDestinationBtc.setText(userNotes.xmrtoDestination);
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " BTC");
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
switch (userNotes.xmrtoTag) {
case "xmrto":
tvXmrToSupport.setVisibility(View.GONE);
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
break;
case "side": // defaults in layout - just add underline
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
tvXmrToSupport.setOnClickListener(v -> {
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
});
break;
default:
tvXmrToSupport.setVisibility(View.GONE);
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
tvXmrToLogo.setVisibility(View.GONE);
}
} else {
cvXmrTo.setVisibility(View.GONE);
}
@@ -298,6 +331,11 @@ public class TxFragment extends Fragment {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), R.attr.colorSurface));
setSharedElementEnterTransition(transform);
}
@Override

File diff suppressed because it is too large Load Diff

View File

@@ -36,12 +36,14 @@ import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
@@ -49,19 +51,21 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import timber.log.Timber;
public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener {
private TransactionInfoAdapter adapter;
private NumberFormat formatter = NumberFormat.getInstance();
private final NumberFormat formatter = NumberFormat.getInstance();
private TextView tvStreetView;
private LinearLayout llBalance;
@@ -78,7 +82,7 @@ public class WalletFragment extends Fragment
private Spinner sCurrency;
private List<String> dismissedTransactions = new ArrayList<>();
private final List<String> dismissedTransactions = new ArrayList<>();
public void resetDismissedTransactions() {
dismissedTransactions.clear();
@@ -91,7 +95,7 @@ public class WalletFragment extends Fragment
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
if (activityCallback.hasWallet())
inflater.inflate(R.menu.wallet_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
@@ -102,6 +106,13 @@ public class WalletFragment extends Fragment
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_wallet, container, false);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
ivStreetGunther = view.findViewById(R.id.ivStreetGunther);
tvStreetView = view.findViewById(R.id.tvStreetView);
llBalance = view.findViewById(R.id.llBalance);
@@ -122,7 +133,7 @@ public class WalletFragment extends Fragment
List<String> currencies = new ArrayList<>();
currencies.add(Helper.BASE_CRYPTO);
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner_balance, currencies);
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(Objects.requireNonNull(getContext()), R.layout.item_spinner_balance, currencies);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
sCurrency.setAdapter(spinnerAdapter);
@@ -153,7 +164,6 @@ public class WalletFragment extends Fragment
dismissedTransactions.add(adapter.getItem(position).hash);
adapter.removeItem(position);
}
adapter.notifyDataSetChanged();
}
@Override
@@ -162,25 +172,14 @@ public class WalletFragment extends Fragment
dismissedTransactions.add(adapter.getItem(position).hash);
adapter.removeItem(position);
}
adapter.notifyDataSetChanged();
}
});
recyclerView.addOnItemTouchListener(swipeTouchListener);
bSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onSendRequest();
}
});
bReceive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onWalletReceive();
}
});
bSend.setOnClickListener(v -> activityCallback.onSendRequest(v));
bReceive.setOnClickListener(v -> activityCallback.onWalletReceive(v));
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
@@ -203,6 +202,16 @@ public class WalletFragment extends Fragment
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
}
void showBalance(String balance) {
tvBalance.setText(balance);
final boolean streetMode = activityCallback.isStreetMode();
@@ -242,7 +251,7 @@ public class WalletFragment extends Fragment
String balanceCurrency = Helper.BASE_CRYPTO;
double balanceRate = 1.0;
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
void refreshBalance() {
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
@@ -260,24 +269,14 @@ public class WalletFragment extends Fragment
@Override
public void onSuccess(final ExchangeRate exchangeRate) {
if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
exchange(exchangeRate);
}
});
new Handler(Looper.getMainLooper()).post(() -> exchange(exchangeRate));
}
@Override
public void onError(final Exception e) {
Timber.e(e.getLocalizedMessage());
if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
exchangeFailed();
}
});
new Handler(Looper.getMainLooper()).post(() -> exchangeFailed());
}
});
} else {
@@ -333,7 +332,13 @@ public class WalletFragment extends Fragment
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
activityCallback.onTxDetailsRequest(infoItem);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
activityCallback.onTxDetailsRequest(view, infoItem);
}
// called from activity
@@ -351,7 +356,6 @@ public class WalletFragment extends Fragment
list.add(info);
}
adapter.setInfos(list);
adapter.notifyDataSetChanged();
}
updateStatus(wallet);
}
@@ -413,7 +417,6 @@ public class WalletFragment extends Fragment
void setActivityTitle(Wallet wallet) {
if (wallet == null) return;
walletTitle = wallet.getName();
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
walletSubtitle = wallet.getAccountLabel();
activityCallback.setTitle(walletTitle, walletSubtitle);
Timber.d("wallet title is %s", walletTitle);
@@ -437,7 +440,7 @@ public class WalletFragment extends Fragment
balance = wallet.getBalance();
unlockedBalance = wallet.getUnlockedBalance();
refreshBalance();
String sync = "";
String sync;
if (!activityCallback.hasBoundService())
throw new IllegalStateException("WalletService not bound.");
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
@@ -478,9 +481,9 @@ public class WalletFragment extends Fragment
long getDaemonHeight(); //mBoundService.getDaemonHeight();
void onSendRequest();
void onSendRequest(View view);
void onTxDetailsRequest(TransactionInfo info);
void onTxDetailsRequest(View view, TransactionInfo info);
boolean isSynced();
@@ -492,7 +495,7 @@ public class WalletFragment extends Fragment
String getTxKey(String txId);
void onWalletReceive();
void onWalletReceive(View view);
boolean hasWallet();
@@ -506,7 +509,7 @@ public class WalletFragment extends Fragment
}
@Override
public void onAttach(Context context) {
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
@@ -549,7 +552,7 @@ public class WalletFragment extends Fragment
//TODO figure out why gunther disappears on return from send although he is still set
if (enable) {
if (streetGunther == null)
streetGunther = ContextCompat.getDrawable(getContext(), R.drawable.ic_gunther_streetmode);
streetGunther = ContextCompat.getDrawable(Objects.requireNonNull(getContext()), R.drawable.ic_gunther_streetmode);
ivStreetGunther.setImageDrawable(streetGunther);
} else
ivStreetGunther.setImageDrawable(null);

View File

@@ -16,14 +16,13 @@
package com.m2049r.xmrwallet;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.util.DayNightMode;
import com.m2049r.xmrwallet.util.LocaleHelper;
import com.m2049r.xmrwallet.util.NightmodeHelper;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
package com.m2049r.xmrwallet.data;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.validator.BitcoinAddressType;
import com.m2049r.xmrwallet.util.validator.BitcoinAddressValidator;
import com.m2049r.xmrwallet.util.validator.EthAddressValidator;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Crypto {
XMR("XMR", true, "monero:tx_amount:recipient_name:tx_description", R.id.ibXMR, R.drawable.ic_monero, R.drawable.ic_monero_bw, Wallet::isAddressValid),
BTC("BTC", true, "bitcoin:amount:label:message", R.id.ibBTC, R.drawable.ic_xmrto_btc, R.drawable.ic_xmrto_btc_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.BTC);
}),
DASH("DASH", true, "dash:amount:label:message", R.id.ibDASH, R.drawable.ic_xmrto_dash, R.drawable.ic_xmrto_dash_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DASH);
}),
DOGE("DOGE", true, "dogecoin:amount:label:message", R.id.ibDOGE, R.drawable.ic_xmrto_doge, R.drawable.ic_xmrto_doge_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DOGE);
}),
ETH("ETH", false, "ethereum:amount:label:message", R.id.ibETH, R.drawable.ic_xmrto_eth, R.drawable.ic_xmrto_eth_off, EthAddressValidator::validate),
LTC("LTC", true, "litecoin:amount:label:message", R.id.ibLTC, R.drawable.ic_xmrto_ltc, R.drawable.ic_xmrto_ltc_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.LTC);
});
@Getter
@NonNull
private final String symbol;
@Getter
private final boolean casefull;
@NonNull
private final String uriSpec;
@Getter
private final int buttonId;
@Getter
private final int iconEnabledId;
@Getter
private final int iconDisabledId;
@NonNull
private final Validator validator;
@Nullable
public static Crypto withScheme(@NonNull String scheme) {
for (Crypto crypto : values()) {
if (crypto.getUriScheme().equals(scheme)) return crypto;
}
return null;
}
@Nullable
public static Crypto withSymbol(@NonNull String symbol) {
final String upperSymbol = symbol.toUpperCase();
for (Crypto crypto : values()) {
if (crypto.symbol.equals(upperSymbol)) return crypto;
}
return null;
}
interface Validator {
boolean validate(String address);
}
// TODO maybe cache these segments
String getUriScheme() {
return uriSpec.split(":")[0];
}
String getUriAmount() {
return uriSpec.split(":")[1];
}
String getUriLabel() {
return uriSpec.split(":")[2];
}
String getUriMessage() {
return uriSpec.split(":")[3];
}
boolean validate(String address) {
return validator.validate(address);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
// Nodes stolen from https://moneroworld.com/#nodes
@AllArgsConstructor
public enum DefaultNodes {
MONERUJO("nodex.monerujo.io:18081"),
XMRTO("node.xmr.to:18081"),
SUPPORTXMR("node.supportxmr.com:18081"),
HASHVAULT("nodes.hashvault.pro:18081"),
MONEROWORLD("node.moneroworld.com:18089"),
XMRTW("opennode.xmr-tw.org:18089");
@Getter
private final String uri;
}

View File

@@ -26,6 +26,8 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import lombok.Getter;
import lombok.Setter;
import timber.log.Timber;
public class Node {
@@ -33,27 +35,43 @@ public class Node {
static public final String STAGENET = "stagenet";
static public final String TESTNET = "testnet";
@Getter
private String name = null;
@Getter
final private NetworkType networkType;
InetAddress hostAddress;
@Getter
private String host;
@Getter
@Setter
int rpcPort = 0;
private int levinPort = 0;
@Getter
@Setter
private String username = "";
@Getter
@Setter
private String password = "";
@Getter
@Setter
private boolean favourite = false;
@Getter
@Setter
private boolean selected = false;
@Override
public int hashCode() {
return hostAddress.hashCode();
}
// Nodes are equal if they are the same host address & are on the same network
// Nodes are equal if they are the same host address:port & are on the same network
@Override
public boolean equals(Object other) {
if (!(other instanceof Node)) return false;
final Node anotherNode = (Node) other;
return (hostAddress.equals(anotherNode.hostAddress) && (networkType == anotherNode.networkType));
return (hostAddress.equals(anotherNode.hostAddress)
&& (rpcPort == anotherNode.rpcPort)
&& (networkType == anotherNode.networkType));
}
static public Node fromString(String nodeString) {
@@ -193,7 +211,6 @@ public class Node {
this.levinPort = socketAddress.getPort();
this.username = "";
this.password = "";
//this.name = socketAddress.getHostName(); // triggers DNS so we don't do it by default
}
public String getAddress() {
@@ -204,14 +221,6 @@ public class Node {
return hostAddress.getHostAddress();
}
public String getHost() {
return host;
}
public int getRpcPort() {
return rpcPort;
}
public void setHost(String host) throws UnknownHostException {
if ((host == null) || (host.isEmpty()))
throw new UnknownHostException("loopback not supported (yet?)");
@@ -219,18 +228,6 @@ public class Node {
this.hostAddress = InetAddress.getByName(host);
}
public void setUsername(String user) {
username = user;
}
public void setPassword(String pass) {
password = pass;
}
public void setRpcPort(int port) {
this.rpcPort = port;
}
public void setName() {
if (name == null)
this.name = hostAddress.getHostName();
@@ -243,30 +240,6 @@ public class Node {
this.name = name;
}
public String getName() {
return name;
}
public NetworkType getNetworkType() {
return networkType;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public boolean isFavourite() {
return favourite;
}
public void setFavourite(boolean favourite) {
this.favourite = favourite;
}
public void toggleFavourite() {
favourite = !favourite;
}

View File

@@ -21,8 +21,8 @@ import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
import com.burgstaller.okhttp.digest.CachingAuthenticator;
import com.burgstaller.okhttp.digest.Credentials;
import com.burgstaller.okhttp.digest.DigestAuthenticator;
import com.m2049r.levin.scanner.Dispatcher;
import com.m2049r.levin.scanner.LevinPeer;
import com.m2049r.xmrwallet.util.NodePinger;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import org.json.JSONException;
@@ -36,6 +36,8 @@ import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
import lombok.Setter;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
@@ -46,14 +48,24 @@ import okhttp3.ResponseBody;
import timber.log.Timber;
public class NodeInfo extends Node {
final static public int MIN_MAJOR_VERSION = 11;
final static public int MIN_MAJOR_VERSION = 14;
final static public String RPC_VERSION = "2.0";
@Getter
private long height = 0;
@Getter
private long timestamp = 0;
@Getter
private int majorVersion = 0;
@Getter
private double responseTime = Double.MAX_VALUE;
@Getter
private int responseCode = 0;
@Getter
private boolean tested = false;
@Getter
@Setter
private boolean selecting = false;
public void clear() {
height = 0;
@@ -61,13 +73,13 @@ public class NodeInfo extends Node {
responseTime = Double.MAX_VALUE;
responseCode = 0;
timestamp = 0;
tested = false;
}
static public NodeInfo fromString(String nodeString) {
try {
return new NodeInfo(nodeString);
} catch (IllegalArgumentException ex) {
Timber.w(ex);
return null;
}
}
@@ -113,26 +125,6 @@ public class NodeInfo extends Node {
super();
}
public long getHeight() {
return height;
}
public long getTimestamp() {
return timestamp;
}
public int getMajorVersion() {
return majorVersion;
}
public double getResponseTime() {
return responseTime;
}
public int getResponseCode() {
return responseCode;
}
public boolean isSuccessful() {
return (responseCode >= 200) && (responseCode < 300);
}
@@ -145,23 +137,20 @@ public class NodeInfo extends Node {
return isSuccessful() && (majorVersion >= MIN_MAJOR_VERSION) && (responseTime < Double.MAX_VALUE);
}
static public Comparator<NodeInfo> BestNodeComparator = new Comparator<NodeInfo>() {
@Override
public int compare(NodeInfo o1, NodeInfo o2) {
if (o1.isValid()) {
if (o2.isValid()) { // both are valid
// higher node wins
int heightDiff = (int) (o2.height - o1.height);
if (Math.abs(heightDiff) > Dispatcher.HEIGHT_WINDOW)
return heightDiff;
// if they are (nearly) equal, faster node wins
return (int) Math.signum(o1.responseTime - o2.responseTime);
} else {
return -1;
}
static public Comparator<NodeInfo> BestNodeComparator = (o1, o2) -> {
if (o1.isValid()) {
if (o2.isValid()) { // both are valid
// higher node wins
int heightDiff = (int) (o2.height - o1.height);
if (heightDiff != 0)
return heightDiff;
// if they are equal, faster node wins
return (int) Math.signum(o1.responseTime - o2.responseTime);
} else {
return 1;
return -1;
}
} else {
return 1;
}
};
@@ -192,7 +181,7 @@ public class NodeInfo extends Node {
}
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
public static final double PING_GOOD = HTTP_TIMEOUT / 3; //ms
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
public static final double PING_BAD = HTTP_TIMEOUT;
@@ -200,7 +189,15 @@ public class NodeInfo extends Node {
return testRpcService(rpcPort);
}
public boolean testRpcService(NodePinger.Listener listener) {
boolean result = testRpcService(rpcPort);
if (listener != null)
listener.publish(this);
return result;
}
private boolean testRpcService(int port) {
Timber.d("Testing %s", toNodeString());
clear();
try {
OkHttpClient client = OkHttpHelper.getEagerClient();
@@ -230,8 +227,7 @@ public class NodeInfo extends Node {
if (response.isSuccessful()) {
ResponseBody respBody = response.body(); // closed through Response object
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
final JSONObject json = new JSONObject(
respBody.string());
final JSONObject json = new JSONObject(respBody.string());
String rpcVersion = json.getString("jsonrpc");
if (!RPC_VERSION.equals(rpcVersion))
return false;
@@ -247,8 +243,9 @@ public class NodeInfo extends Node {
}
}
} catch (IOException | JSONException ex) {
// failure
Timber.d(ex.getMessage());
Timber.d(ex);
} finally {
tested = true;
}
return false;
}

View File

@@ -20,6 +20,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet;
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
public class TxData implements Parcelable {
@@ -52,6 +53,10 @@ public class TxData implements Parcelable {
return amount;
}
public double getAmountAsDouble() {
return 1.0 * amount / 1000000000000L;
}
public int getMixin() {
return mixin;
}
@@ -68,6 +73,10 @@ public class TxData implements Parcelable {
this.amount = amount;
}
public void setAmount(double amount) {
this.amount = Wallet.getAmountFromDouble(amount);
}
public void setMixin(int mixin) {
this.mixin = mixin;
}

View File

@@ -18,11 +18,23 @@ package com.m2049r.xmrwallet.data;
import android.os.Parcel;
public class TxDataBtc extends TxData {
import androidx.annotation.NonNull;
private String xmrtoUuid;
import lombok.Getter;
import lombok.Setter;
public class TxDataBtc extends TxData {
@Getter
@Setter
private String btcSymbol; // the actual non-XMR thing we're sending
@Getter
@Setter
private String xmrtoOrderId; // shown in success screen
@Getter
@Setter
private String btcAddress;
private String bip70;
@Getter
@Setter
private double btcAmount;
public TxDataBtc() {
@@ -33,44 +45,12 @@ public class TxDataBtc extends TxData {
super(txDataBtc);
}
public String getXmrtoUuid() {
return xmrtoUuid;
}
public void setXmrtoUuid(String xmrtoUuid) {
this.xmrtoUuid = xmrtoUuid;
}
public String getBtcAddress() {
return btcAddress;
}
public void setBtcAddress(String btcAddress) {
this.btcAddress = btcAddress;
}
public String getBip70() {
return bip70;
}
public void setBip70(String bip70) {
this.bip70 = bip70;
}
public double getBtcAmount() {
return btcAmount;
}
public void setBtcAmount(double btcAmount) {
this.btcAmount = btcAmount;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(xmrtoUuid);
out.writeString(btcSymbol);
out.writeString(xmrtoOrderId);
out.writeString(btcAddress);
out.writeString(bip70);
out.writeDouble(btcAmount);
}
@@ -87,23 +67,35 @@ public class TxDataBtc extends TxData {
protected TxDataBtc(Parcel in) {
super(in);
xmrtoUuid = in.readString();
btcSymbol = in.readString();
xmrtoOrderId = in.readString();
btcAddress = in.readString();
bip70 = in.readString();
btcAmount = in.readDouble();
}
@NonNull
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(",xmrtoUuid:");
sb.append(xmrtoUuid);
sb.append("xmrtoOrderId:");
sb.append(xmrtoOrderId);
sb.append(",btcSymbol:");
sb.append(btcSymbol);
sb.append(",btcAddress:");
sb.append(btcAddress);
sb.append(",bip70:");
sb.append(bip70);
sb.append(",btcAmount:");
sb.append(btcAmount);
return sb.toString();
}
public boolean validateAddress(@NonNull String address) {
if ((btcSymbol == null) || (btcAddress == null)) return false;
final Crypto crypto = Crypto.withSymbol(btcSymbol);
if (crypto == null) return false;
if (crypto.isCasefull()) { // compare as-is
return address.equals(btcAddress);
} else { // normalize & compare (e.g. ETH with and without checksum capitals
return address.toLowerCase().equals(btcAddress.toLowerCase());
}
}
}

View File

@@ -16,18 +16,19 @@
package com.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
import com.m2049r.xmrwallet.util.Helper;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import timber.log.Timber;
public class UserNotes {
public String txNotes = "";
public String note = "";
public String xmrtoTag = null;
public String xmrtoKey = null;
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
public String xmrtoCurrency = null;
public String xmrtoDestination = null;
public UserNotes(final String txNotes) {
@@ -35,13 +36,15 @@ public class UserNotes {
return;
}
this.txNotes = txNotes;
Pattern p = Pattern.compile("^\\{(xmrto-\\w{6}),([0-9.]*)BTC,(\\w*)\\} ?(.*)");
Pattern p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)([A-Z]+),(\\w*)\\} ?(.*)");
Matcher m = p.matcher(txNotes);
if (m.find()) {
xmrtoKey = m.group(1);
xmrtoAmount = m.group(2);
xmrtoDestination = m.group(3);
note = m.group(4);
xmrtoTag = m.group(1);
xmrtoKey = m.group(2);
xmrtoAmount = m.group(3);
xmrtoCurrency = m.group(4);
xmrtoDestination = m.group(5);
note = m.group(6);
} else {
note = txNotes;
}
@@ -56,12 +59,15 @@ public class UserNotes {
txNotes = buildTxNote();
}
public void setXmrtoStatus(QueryOrderStatus xmrtoStatus) {
if (xmrtoStatus != null) {
xmrtoKey = xmrtoStatus.getUuid();
xmrtoAmount = String.valueOf(xmrtoStatus.getBtcAmount());
xmrtoDestination = xmrtoStatus.getBtcDestAddress();
public void setXmrtoOrder(CreateOrder order) {
if (order != null) {
xmrtoTag = order.TAG;
xmrtoKey = order.getOrderId();
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
xmrtoCurrency = order.getBtcCurrency();
xmrtoDestination = order.getBtcAddress();
} else {
xmrtoTag = null;
xmrtoKey = null;
xmrtoAmount = null;
xmrtoDestination = null;
@@ -70,15 +76,18 @@ public class UserNotes {
}
private String buildTxNote() {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
if (xmrtoKey != null) {
if ((xmrtoAmount == null) || (xmrtoDestination == null))
throw new IllegalArgumentException("Broken notes");
sb.append("{");
sb.append(xmrtoTag);
sb.append("-");
sb.append(xmrtoKey);
sb.append(",");
sb.append(xmrtoAmount);
sb.append("BTC,");
sb.append(xmrtoCurrency);
sb.append(",");
sb.append(xmrtoDestination);
sb.append("}");
if ((note != null) && (!note.isEmpty()))

View File

@@ -27,7 +27,6 @@ import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;

View File

@@ -42,9 +42,8 @@ public class SendAmountWizardFragment extends SendWizardFragment {
Listener sendListener;
public SendAmountWizardFragment setSendListener(Listener listener) {
public void setSendListener(Listener listener) {
this.sendListener = listener;
return this;
}
interface Listener {

View File

@@ -16,6 +16,9 @@
package com.m2049r.xmrwallet.fragment.send;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -27,15 +30,17 @@ import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.ShiftException;
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderStatus;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.xmrto.XmrToException;
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
import com.m2049r.xmrwallet.util.ServiceHelper;
import java.text.NumberFormat;
import java.util.Locale;
@@ -52,23 +57,23 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
SendSuccessWizardFragment.Listener sendListener;
public SendBtcSuccessWizardFragment setSendListener(SendSuccessWizardFragment.Listener listener) {
public void setSendListener(SendSuccessWizardFragment.Listener listener) {
this.sendListener = listener;
return this;
}
ImageButton bCopyTxId;
private TextView tvTxId;
private TextView tvTxAddress;
private TextView tvTxPaymentId;
private TextView tvTxAmount;
private TextView tvTxFee;
private TextView tvXmrToAmount;
private ImageView ivXmrToIcon;
private TextView tvXmrToStatus;
private ImageView ivXmrToStatus;
private ImageView ivXmrToStatusBig;
private ProgressBar pbXmrto;
private TextView tvTxXmrToKey;
private TextView tvXmrToSupport;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -87,13 +92,13 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
});
tvXmrToAmount = view.findViewById(R.id.tvXmrToAmount);
ivXmrToIcon = view.findViewById(R.id.ivXmrToIcon);
tvXmrToStatus = view.findViewById(R.id.tvXmrToStatus);
ivXmrToStatus = view.findViewById(R.id.ivXmrToStatus);
ivXmrToStatusBig = view.findViewById(R.id.ivXmrToStatusBig);
tvTxId = view.findViewById(R.id.tvTxId);
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
@@ -101,14 +106,14 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
}
tvTxXmrToKey.setOnClickListener(v -> {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
});
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
return view;
}
@@ -147,9 +152,16 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
NumberFormat df = NumberFormat.getInstance(Locale.US);
df.setMaximumFractionDigits(12);
String btcAmount = df.format(btcData.getBtcAmount());
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount));
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount, btcData.getBtcSymbol()));
//TODO btcData.getBtcAddress();
tvTxXmrToKey.setText(btcData.getXmrtoUuid());
tvTxXmrToKey.setText(btcData.getXmrtoOrderId());
final Crypto crypto = Crypto.withSymbol(btcData.getBtcSymbol());
ivXmrToIcon.setImageResource(crypto.getIconEnabledId());
tvXmrToSupport.setOnClickListener(v -> {
Uri orderUri = getXmrToApi().getQueryOrderUri(btcData.getXmrtoOrderId());
Intent intent = new Intent(Intent.ACTION_VIEW, orderUri);
startActivity(intent);
});
queryOrder();
} else {
throw new IllegalStateException("btcData is null");
@@ -158,33 +170,23 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
sendListener.enableDone();
}
private final int QUERY_INTERVAL = 1000; // ms
private void processQueryOrder(final QueryOrderStatus status) {
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getUuid());
if (!btcData.getXmrtoUuid().equals(status.getUuid()))
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getOrderId());
if (!btcData.getXmrtoOrderId().equals(status.getOrderId()))
throw new IllegalStateException("UUIDs do not match!");
if (isResumed && (getView() != null))
getView().post(new Runnable() {
@Override
public void run() {
showXmrToStatus(status);
if (!status.isTerminal()) {
getView().postDelayed(new Runnable() {
@Override
public void run() {
queryOrder();
}
}, QUERY_INTERVAL);
}
getView().post(() -> {
showXmrToStatus(status);
if (!status.isTerminal()) {
getView().postDelayed(this::queryOrder, SideShiftApi.QUERY_INTERVAL);
}
});
}
private void queryOrder() {
Timber.d("queryOrder(%s)", btcData.getXmrtoUuid());
Timber.d("queryOrder(%s)", btcData.getXmrtoOrderId());
if (!isResumed) return;
getXmrToApi().queryOrderStatus(btcData.getXmrtoUuid(), new XmrToCallback<QueryOrderStatus>() {
getXmrToApi().queryOrderStatus(btcData.getXmrtoOrderId(), new ShiftCallback<QueryOrderStatus>() {
@Override
public void onSuccess(QueryOrderStatus status) {
if (!isAdded()) return;
@@ -194,38 +196,34 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
@Override
public void onError(final Exception ex) {
if (!isResumed) return;
Timber.e(ex);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (ex instanceof XmrToException) {
Toast.makeText(getActivity(), ((XmrToException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
Timber.w(ex);
getActivity().runOnUiThread(() -> {
if (ex instanceof ShiftException) {
Toast.makeText(getActivity(), ((ShiftException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
});
}
});
}
private int statusResource = 0;
void showXmrToStatus(final QueryOrderStatus status) {
int statusResource = 0;
if (status.isError()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
statusResource = R.drawable.ic_error_red_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isSent()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent));
} else if (status.isSent() || status.isPaid()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol()));
statusResource = R.drawable.ic_success_green_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isWaiting()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
statusResource = R.drawable.ic_pending_orange_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isPending()) {
if (status.isPaid()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
} else {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
}
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
statusResource = R.drawable.ic_pending_orange_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
} else {
@@ -234,20 +232,21 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
ivXmrToStatus.setImageResource(statusResource);
if (status.isTerminal()) {
pbXmrto.setVisibility(View.INVISIBLE);
ivXmrToIcon.setVisibility(View.GONE);
ivXmrToStatus.setVisibility(View.GONE);
ivXmrToStatusBig.setImageResource(statusResource);
ivXmrToStatusBig.setVisibility(View.VISIBLE);
}
}
private XmrToApi xmrToApi = null;
private SideShiftApi xmrToApi = null;
private final XmrToApi getXmrToApi() {
private SideShiftApi getXmrToApi() {
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
ServiceHelper.getXmrToBaseUrl());
}
}
}

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