1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-03 08:23:04 +02:00

Compare commits

...

28 Commits

Author SHA1 Message Date
m2049r
b01de1ad6e bump version 2021-03-20 09:14:51 +01:00
anhdres
5fb1bcb552 Update strings.xml (#732)
several gender mistakes ("monedero" we're using now is masculine)
ledger translations
onboarding
sideshift integration
2021-03-19 23:38:03 +01:00
anhdres
b937ba38b6 Update help.xml (#731)
translated new text and help_ok string
2021-03-19 23:36:32 +01:00
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
85 changed files with 1503 additions and 1718 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

@@ -2,13 +2,13 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
buildToolsVersion '29.0.3'
defaultConfig {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 29
versionCode 705
versionName "1.17.5.1 'Druk'"
versionCode 713
versionName "1.17.13 'Druk'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

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()));

View File

@@ -59,7 +59,7 @@ import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.WalletService;
import com.m2049r.xmrwallet.util.ColorHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.DayNightMode;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
@@ -78,7 +78,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -376,7 +375,7 @@ public class LoginActivity extends BaseActivity
}
@Override
public void fail(String walletName1, String password, boolean fingerprintUsed) {
public void fail(String walletName) {
}
});
} else { // this cannot really happen as we prefilter choices
@@ -412,7 +411,7 @@ public class LoginActivity extends BaseActivity
}
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
public void fail(String walletName) {
}
});
} else { // this cannot really happen as we prefilter choices
@@ -707,11 +706,11 @@ public class LoginActivity extends BaseActivity
break;
case NetworkType_Testnet:
toolbar.setSubtitle(getString(R.string.connect_testnet));
toolbar.setBackgroundResource(ColorHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
break;
case NetworkType_Stagenet:
toolbar.setSubtitle(getString(R.string.connect_stagenet));
toolbar.setBackgroundResource(ColorHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
break;
default:
throw new IllegalStateException("NetworkType unknown: " + net);
@@ -1400,7 +1399,7 @@ public class LoginActivity extends BaseActivity
}
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
public void fail(String walletName) {
}
});

View File

@@ -258,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()) {

View File

@@ -524,7 +524,7 @@ public class NodeFragment extends Fragment
.setNegativeButton(getString(R.string.label_cancel),
(dialog, id) -> {
closeDialog();
nodesAdapter.dataSetChanged(); // to refresh test results
nodesAdapter.setNodes(); // to refresh test results
});
editDialog = alertDialogBuilder.create();
@@ -588,7 +588,7 @@ public class NodeFragment extends Fragment
if (nodeBackup == null) {
nodesAdapter.addNode(nodeInfo);
} else {
nodesAdapter.dataSetChanged();
nodesAdapter.setNodes();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -36,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;
@@ -300,7 +302,7 @@ public class TxFragment extends Fragment {
}
tvTxXmrToKey.setText(key);
tvDestinationBtc.setText(userNotes.xmrtoDestination);
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
switch (userNotes.xmrtoTag) {
case "xmrto":
tvXmrToSupport.setVisibility(View.GONE);
@@ -329,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;
@@ -56,13 +58,14 @@ 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;
@@ -79,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();
@@ -92,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);
@@ -103,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);
@@ -123,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);
@@ -154,7 +164,6 @@ public class WalletFragment extends Fragment
dismissedTransactions.add(adapter.getItem(position).hash);
adapter.removeItem(position);
}
adapter.notifyDataSetChanged();
}
@Override
@@ -163,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
@@ -204,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();
@@ -261,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 {
@@ -334,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
@@ -352,7 +356,6 @@ public class WalletFragment extends Fragment
list.add(info);
}
adapter.setInfos(list);
adapter.notifyDataSetChanged();
}
updateStatus(wallet);
}
@@ -414,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);
@@ -438,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();
@@ -479,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();
@@ -493,7 +495,7 @@ public class WalletFragment extends Fragment
String getTxKey(String txId);
void onWalletReceive();
void onWalletReceive(View view);
boolean hasWallet();
@@ -507,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;
@@ -550,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

@@ -84,7 +84,9 @@ public class BarcodeData {
public String getUriString() {
if (asset != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
StringBuilder sb = new StringBuilder();
sb.append(Crypto.XMR.getUriScheme()).append(address);
sb.append(Crypto.XMR.getUriScheme())
.append(':')
.append(address);
boolean first = true;
if ((description != null) && !description.isEmpty()) {
sb.append(first ? "?" : "&");

View File

@@ -64,12 +64,14 @@ public class Node {
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) {

View File

@@ -227,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;

View File

@@ -332,7 +332,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
send();
}
public void fail(String walletName, String password, boolean fingerprintUsed) {
public void fail(String walletName) {
getActivity().runOnUiThread(() -> {
bSend.setEnabled(sendCountdown > 0); // allow to try again
});

View File

@@ -227,7 +227,7 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
send();
}
public void fail(String walletName, String password, boolean fingerprintUsed) {
public void fail(String walletName) {
getActivity().runOnUiThread(() -> {
bSend.setEnabled(true); // allow to try again
});

View File

@@ -36,6 +36,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.OnBackPressedListener;
import com.m2049r.xmrwallet.OnUriScannedListener;
import com.m2049r.xmrwallet.R;
@@ -49,10 +50,12 @@ import com.m2049r.xmrwallet.layout.SpendViewPager;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.DotBar;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.lang.ref.WeakReference;
import java.util.Objects;
import timber.log.Timber;
@@ -309,7 +312,7 @@ public class SendFragment extends Fragment
SparseArray<WeakReference<SendWizardFragment>> myFragments = new SparseArray<>();
public SpendPagerAdapter(FragmentManager fm) {
super(fm);
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
public void addSuccess() {
@@ -538,14 +541,18 @@ public class SendFragment extends Fragment
enableNavigation();
}
@Override
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(Objects.requireNonNull(getContext()), R.attr.colorSurface));
setSharedElementEnterTransition(transform);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.send_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2021 yorha-0x
*
* 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.layout;
import androidx.recyclerview.widget.DiffUtil;
import java.util.List;
public abstract class DiffCallback<T> extends DiffUtil.Callback {
protected final List<T> mOldList;
protected final List<T> mNewList;
public DiffCallback(List<T> oldList, List<T> newList) {
this.mOldList = oldList;
this.mNewList = newList;
}
@Override
public int getOldListSize() {
return mOldList.size();
}
@Override
public int getNewListSize() {
return mNewList.size();
}
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
}

View File

@@ -25,11 +25,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.util.ColorHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.Helper;
import java.net.HttpURLConnection;
@@ -53,7 +54,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
private final List<NodeInfo> nodeItems = new ArrayList<>();
private final OnInteractionListener listener;
private Context context;
private final Context context;
public NodeInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context;
@@ -63,11 +64,34 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
TS_FORMATTER.setTimeZone(tz);
}
private static class NodeDiff extends DiffCallback<NodeInfo> {
public NodeDiff(List<NodeInfo> oldList, List<NodeInfo> newList) {
super(oldList, newList);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
final NodeInfo oldItem = mOldList.get(oldItemPosition);
final NodeInfo newItem = mNewList.get(newItemPosition);
return (oldItem.getTimestamp() == newItem.getTimestamp())
&& (oldItem.isTested() == newItem.isTested())
&& (oldItem.isValid() == newItem.isValid())
&& (oldItem.getResponseTime() == newItem.getResponseTime())
&& (oldItem.isSelected() == newItem.isSelected())
&& (oldItem.getName().equals(newItem.getName()));
}
}
@Override
public @NonNull
ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_node, parent, false);
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_node, parent, false);
return new ViewHolder(view);
}
@@ -82,25 +106,29 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
}
public void addNode(NodeInfo node) {
List<NodeInfo> newItems = new ArrayList<>(nodeItems);
if (!nodeItems.contains(node))
nodeItems.add(node);
dataSetChanged(); // in case the nodeinfo has changed
newItems.add(node);
setNodes(newItems); // in case the nodeinfo has changed
}
public void dataSetChanged() {
Collections.sort(nodeItems, NodeInfo.BestNodeComparator);
notifyDataSetChanged();
}
public void setNodes(Collection<NodeInfo> data) {
nodeItems.clear();
if (data != null) {
for (NodeInfo node : data) {
if (!nodeItems.contains(node))
nodeItems.add(node);
}
public void setNodes(Collection<NodeInfo> newItemsCollection) {
List<NodeInfo> newItems;
if (newItemsCollection != null) {
newItems = new ArrayList<>(newItemsCollection);
Collections.sort(newItems, NodeInfo.BestNodeComparator);
} else {
newItems = new ArrayList<>();
}
dataSetChanged();
final NodeInfoAdapter.NodeDiff diffCallback = new NodeInfoAdapter.NodeDiff(nodeItems, newItems);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
nodeItems.clear();
nodeItems.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
public void setNodes() {
setNodes(nodeItems);
}
private boolean itemsClickable = true;
@@ -130,7 +158,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
showStar();
if (!nodeItem.isFavourite()) {
nodeItem.setSelected(false);
notifyDataSetChanged();
setNodes(nodeItems);
}
});
itemView.setOnClickListener(this);
@@ -154,7 +182,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
Helper.showTimeDifference(tvIp, nodeItem.getTimestamp());
} else {
tvIp.setText(getResponseErrorText(context, nodeItem.getResponseCode()));
tvIp.setTextColor(ColorHelper.getThemedColor(tvIp.getContext(), R.attr.colorError));
tvIp.setTextColor(ThemeHelper.getThemedColor(context, R.attr.colorError));
}
} else {
tvIp.setText(context.getResources().getString(R.string.node_testing, nodeItem.getHostAddress()));

View File

@@ -23,7 +23,9 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.m2049r.xmrwallet.R;
@@ -38,6 +40,7 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
import timber.log.Timber;
@@ -46,10 +49,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
private final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private int outboundColour;
private int inboundColour;
private int pendingColour;
private int failedColour;
private final int outboundColour;
private final int inboundColour;
private final int pendingColour;
private final int failedColour;
public interface OnInteractionListener {
void onInteraction(View view, TransactionInfo item);
@@ -58,7 +61,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
private final List<TransactionInfo> infoItems;
private final OnInteractionListener listener;
private Context context;
private final Context context;
public TransactionInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context;
@@ -73,10 +76,32 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
DATETIME_FORMATTER.setTimeZone(tz);
}
private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> {
public TransactionInfoDiff(List<TransactionInfo> oldList, List<TransactionInfo> newList) {
super(oldList, newList);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).hash.equals(mNewList.get(newItemPosition).hash);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
final TransactionInfo oldItem = mOldList.get(oldItemPosition);
final TransactionInfo newItem = mNewList.get(newItemPosition);
return (oldItem.direction == newItem.direction)
&& (oldItem.isPending == newItem.isPending)
&& (oldItem.isFailed == newItem.isFailed)
&& (Objects.equals(oldItem.notes, newItem.notes));
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_transaction, parent, false);
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transaction, parent, false);
return new ViewHolder(view);
}
@@ -90,23 +115,26 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
return infoItems.size();
}
public void setInfos(List<TransactionInfo> data) {
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
// as the TransactionInfo items are always recreated, we cannot recycle
infoItems.clear();
if (data != null) {
Timber.d("setInfos %s", data.size());
infoItems.addAll(data);
Collections.sort(infoItems);
} else {
public void setInfos(List<TransactionInfo> newItems) {
if (newItems == null) {
newItems = new ArrayList<>();
Timber.d("setInfos null");
} else {
Timber.d("setInfos %s", newItems.size());
}
notifyDataSetChanged();
Collections.sort(newItems);
final DiffCallback<TransactionInfo> diffCallback = new TransactionInfoAdapter.TransactionInfoDiff(infoItems, newItems);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
infoItems.clear();
infoItems.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
public void removeItem(int position) {
infoItems.remove(position);
notifyItemRemoved(position);
List<TransactionInfo> newItems = new ArrayList<>(infoItems);
if (newItems.size() > position)
newItems.remove(position);
setInfos(newItems); // in case the nodeinfo has changed
}
public TransactionInfo getItem(int position) {
@@ -139,7 +167,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
}
void bind(int position) {
this.infoItem = infoItems.get(position);
infoItem = infoItems.get(position);
itemView.setTransitionName(context.getString(R.string.tx_item_transition_name, infoItem.hash));
UserNotes userNotes = new UserNotes(infoItem.notes);
if (userNotes.xmrtoKey != null) {

View File

@@ -17,8 +17,6 @@
package com.m2049r.xmrwallet.layout;
import android.content.Context;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -26,6 +24,11 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -63,6 +66,24 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
DATETIME_FORMATTER.setTimeZone(tz);
}
private static class WalletInfoDiff extends DiffCallback<WalletManager.WalletInfo> {
public WalletInfoDiff(List<WalletManager.WalletInfo> oldList, List<WalletManager.WalletInfo> newList) {
super(oldList, newList);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).name.equals(mNewList.get(newItemPosition).name);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).compareTo(mNewList.get(newItemPosition)) == 0;
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
@@ -84,18 +105,19 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
return infoItems.get(position);
}
public void setInfos(List<WalletManager.WalletInfo> data) {
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
// as the WalletInfo items are always recreated, we cannot recycle
infoItems.clear();
if (data != null) {
Timber.d("setInfos %s", data.size());
infoItems.addAll(data);
Collections.sort(infoItems);
} else {
public void setInfos(List<WalletManager.WalletInfo> newItems) {
if (newItems == null) {
newItems = new ArrayList<>();
Timber.d("setInfos null");
} else {
Timber.d("setInfos %s", newItems.size());
}
notifyDataSetChanged();
Collections.sort(newItems);
final DiffCallback<WalletManager.WalletInfo> diffCallback = new WalletInfoAdapter.WalletInfoDiff(infoItems, newItems);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
infoItems.clear();
infoItems.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@@ -110,35 +132,24 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
tvName = itemView.findViewById(R.id.tvName);
tvAddress = itemView.findViewById(R.id.tvAddress);
ibOptions = itemView.findViewById(R.id.ibOptions);
ibOptions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (popupOpen) return;
//creating a popup menu
PopupMenu popup = new PopupMenu(context, ibOptions);
//inflating menu from xml resource
popup.inflate(R.menu.list_context_menu);
popupOpen = true;
//adding click listener
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (listener != null) {
return listener.onContextInteraction(item, infoItem);
}
return false;
}
});
//displaying the popup
popup.show();
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
popupOpen = false;
}
});
ibOptions.setOnClickListener(view -> {
if (popupOpen) return;
//creating a popup menu
PopupMenu popup = new PopupMenu(context, ibOptions);
//inflating menu from xml resource
popup.inflate(R.menu.list_context_menu);
popupOpen = true;
//adding click listener
popup.setOnMenuItemClickListener(item -> {
if (listener != null) {
return listener.onContextInteraction(item, infoItem);
}
return false;
});
//displaying the popup
popup.show();
popup.setOnDismissListener(menu -> popupOpen = false);
}
});
itemView.setOnClickListener(this);
}

View File

@@ -23,7 +23,6 @@ import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -59,9 +58,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
import java.io.File;
import java.io.IOException;
@@ -77,7 +74,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.HttpsURLConnection;
import okhttp3.HttpUrl;
import timber.log.Timber;
public class Helper {
@@ -487,7 +483,6 @@ public class Helper {
}
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (etPassword.getError() != null) {
@@ -511,17 +506,16 @@ public class Helper {
.setCancelable(false)
.setPositiveButton(context.getString(R.string.label_ok), null)
.setNegativeButton(context.getString(R.string.label_cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Helper.hideKeyboardAlways((Activity) context);
cancelSignal.cancel();
if (passwordTask != null) {
passwordTask.cancel(true);
passwordTask = null;
}
dialog.cancel();
openDialog = null;
(dialog, id) -> {
action.fail(wallet);
Helper.hideKeyboardAlways((Activity) context);
cancelSignal.cancel();
if (passwordTask != null) {
passwordTask.cancel(true);
passwordTask = null;
}
dialog.cancel();
openDialog = null;
});
openDialog = alertDialogBuilder.create();
@@ -564,45 +558,37 @@ public class Helper {
};
}
openDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
if (fingerprintAuthAllowed && fingerprintAuthCallback != null) {
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
tvOpenPrompt.setVisibility(View.VISIBLE);
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
} else {
etPassword.requestFocus();
}
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String pass = etPassword.getEditText().getText().toString();
if (passwordTask == null) {
passwordTask = new PasswordTask(pass, false);
passwordTask.execute();
}
}
});
openDialog.setOnShowListener(dialog -> {
if (fingerprintAuthAllowed && fingerprintAuthCallback != null) {
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
tvOpenPrompt.setVisibility(View.VISIBLE);
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
} else {
etPassword.requestFocus();
}
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view -> {
String pass = etPassword.getEditText().getText().toString();
if (passwordTask == null) {
passwordTask = new PasswordTask(pass, false);
passwordTask.execute();
}
});
});
// accept keyboard "ok"
etPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
String pass = etPassword.getEditText().getText().toString();
if (passwordTask == null) {
passwordTask = new PasswordTask(pass, false);
passwordTask.execute();
}
return true;
etPassword.getEditText().setOnEditorActionListener((v, actionId, event) -> {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
String pass = etPassword.getEditText().getText().toString();
if (passwordTask == null) {
passwordTask = new PasswordTask(pass, false);
passwordTask.execute();
}
return false;
return true;
}
return false;
});
if (Helper.preventScreenshot()) {
@@ -616,7 +602,7 @@ public class Helper {
public interface PasswordAction {
void act(String walletName, String password, boolean fingerprintUsed);
void fail(String walletName, String password, boolean fingerprintUsed);
void fail(String walletName);
}
static private boolean processPasswordEntry(Context context, String walletName, String pass, boolean fingerprintUsed, PasswordAction action) {
@@ -625,7 +611,7 @@ public class Helper {
action.act(walletName, walletPassword, fingerprintUsed);
return true;
} else {
action.fail(walletName, walletPassword, fingerprintUsed);
action.fail(walletName);
return false;
}
}
@@ -670,8 +656,8 @@ public class Helper {
}
view.setText(msg);
if (hours >= STALE_NODE_HOURS)
view.setTextColor(ColorHelper.getThemedColor(view.getContext(), R.attr.colorError));
view.setTextColor(ThemeHelper.getThemedColor(view.getContext(), R.attr.colorError));
else
view.setTextColor(ColorHelper.getThemedColor(view.getContext(), android.R.attr.textColorPrimary));
view.setTextColor(ThemeHelper.getThemedColor(view.getContext(), android.R.attr.textColorPrimary));
}
}

View File

@@ -121,6 +121,10 @@ public class RestoreHeight {
blockheight.put("2020-09-01", 2176790L);
blockheight.put("2020-10-01", 2198370L);
blockheight.put("2020-11-01", 2220670L);
blockheight.put("2020-12-01", 2242241L);
blockheight.put("2021-01-01", 2264584L);
blockheight.put("2021-02-01", 2286892L);
blockheight.put("2021-03-01", 2307079L);
}
public long getHeight(String date) {

View File

@@ -17,19 +17,26 @@
package com.m2049r.xmrwallet.util;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.TypedValue;
import com.m2049r.xmrwallet.R;
import androidx.annotation.ColorInt;
public class ColorHelper {
public class ThemeHelper {
static public int getThemedResourceId(Context ctx, int attrId) {
TypedArray styledAttributes = ctx.getTheme().obtainStyledAttributes(R.style.MyMaterialTheme, new int[]{attrId});
return styledAttributes.getResourceId(0, 0);
final TypedValue typedValue = new TypedValue();
if (ctx.getTheme().resolveAttribute(attrId, typedValue, true))
return typedValue.resourceId;
else
return 0;
}
@ColorInt
static public int getThemedColor(Context ctx, int attrId) {
TypedArray styledAttributes = ctx.getTheme().obtainStyledAttributes(R.style.MyMaterialTheme, new int[]{attrId});
return styledAttributes.getColor(0, Color.BLACK);
final TypedValue typedValue = new TypedValue();
if (ctx.getTheme().resolveAttribute(attrId, typedValue, true))
return typedValue.data;
else
return Color.BLACK;
}
}

View File

@@ -42,7 +42,7 @@ import com.m2049r.xmrwallet.model.Wallet;
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.ColorHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ServiceHelper;
@@ -180,7 +180,7 @@ public class ExchangeView extends LinearLayout {
// make progress circle gray
pbExchange.getIndeterminateDrawable().
setColorFilter(ColorHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
setColorFilter(ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY);
sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

View File

@@ -0,0 +1,73 @@
package com.m2049r.xmrwallet.widget;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.R;
import com.nulabinc.zxcvbn.Zxcvbn;
public class PasswordEntryView extends TextInputLayout implements TextWatcher {
final private Zxcvbn zxcvbn = new Zxcvbn();
public PasswordEntryView(@NonNull Context context) {
super(context, null);
}
public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs, R.attr.textInputStyle);
}
public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void addView(@NonNull View child, int index, @NonNull final ViewGroup.LayoutParams params) {
super.addView(child, index, params);
final EditText et = getEditText();
if (et != null)
et.addTextChangedListener(this);
}
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String password = s.toString();
int icon = 0;
if (!password.isEmpty()) {
final double strength = Math.min(zxcvbn.measure(password).getGuessesLog10(), 15) / 3 * 20; // 0-100%
if (strength < 21)
icon = R.drawable.ic_smiley_sad_filled;
else if (strength < 40)
icon = R.drawable.ic_smiley_meh_filled;
else if (strength < 60)
icon = R.drawable.ic_smiley_neutral_filled;
else if (strength < 80)
icon = R.drawable.ic_smiley_happy_filled;
else if (strength < 99)
icon = R.drawable.ic_smiley_ecstatic_filled;
else
icon = R.drawable.ic_smiley_gunther_filled;
}
setErrorIconDrawable(icon);
if (icon != 0)
setError(" ");
else setError(null);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="85.43"
android:viewportHeight="85.43">
<path
android:fillColor="@color/monerujoPrimary"
android:pathData="M77.39,26A38.49,38.49 0,0 0,8 26Z" />
<path
android:fillColor="@color/monerujoPrimary"
android:pathData="M79.54,33c-2.3,8.19 -9.06,14.12 -17.06,14.12S47.73,41.19 45.43,33L41.34,33C39,41.19 31.93,47.12 23.63,47.12S8.31,41.19 5.92,33L5.46,33A38.5,38.5 0,1 0,80 33ZM34.65,63.54L12.24,63.54C20,51.84 29.09,49 32.86,48.34a8.44,8.44 0,0 1,1.6 -0.21h0.19a7.71,7.71 0,0 1,0 15.42ZM50.42,63.54a7.71,7.71 0,0 1,0 -15.42h0.19a8.34,8.34 0,0 1,1.6 0.21c3.78,0.68 12.87,3.5 20.62,15.2Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="85.43"
android:viewportHeight="85.43">
<path
android:fillColor="@color/monerujoPrimary"
android:pathData="M42.8,4.22A38.5,38.5 0,1 0,81.29 42.71,38.54 38.54,0 0,0 42.8,4.22ZM58.21,28.5a5.29,5.29 0,1 1,-5.28 5.28A5.28,5.28 0,0 1,58.21 28.5ZM27.39,28.5a5.29,5.29 0,1 1,-5.29 5.28A5.28,5.28 0,0 1,27.39 28.5ZM64.39,50.11c-1.14,5.82 -7.12,16.83 -21.59,16.83s-20.45,-11 -21.59,-16.83a3.5,3.5 0,1 1,6.87 -1.34c0.23,1.12 2.68,11.17 14.72,11.17 12.39,0 14.63,-10.72 14.72,-11.17a3.5,3.5 0,1 1,6.87 1.34Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="85.43"
android:viewportHeight="85.43">
<path
android:fillColor="@color/monerujoPrimary"
android:pathData="M42.71,4.22a38.5,38.5 0,1 0,38.5 38.49A38.54,38.54 0,0 0,42.71 4.22ZM22,33.78a5.28,5.28 0,1 1,5.28 5.29A5.27,5.27 0,0 1,22 33.78ZM58.83,53.78L28,60.24a3.5,3.5 0,0 1,-1.44 -6.85L57.4,46.9a3.5,3.5 0,0 1,1.45 6.85ZM58.11,39.1a5.29,5.29 0,1 1,5.28 -5.29A5.29,5.29 0,0 1,58.13 39.07Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="85.43"
android:viewportHeight="85.43">
<path
android:fillColor="@color/monerujoPrimary"
android:pathData="M42.84,4.22a38.5,38.5 0,1 0,38.5 38.49A38.54,38.54 0,0 0,42.84 4.22ZM22.15,33.78a5.28,5.28 0,1 1,5.28 5.29A5.27,5.27 0,0 1,22.15 33.78ZM58.26,56.84L27.43,56.84a3.5,3.5 0,0 1,0 -7L58.26,49.84a3.5,3.5 0,0 1,0 7ZM58.26,39.07a5.29,5.29 0,1 1,5.28 -5.29A5.29,5.29 0,0 1,58.26 39.07Z" />
</vector>

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