1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-05 09:58:42 +02:00

Compare commits

..

25 Commits

Author SHA1 Message Date
m2049r
cd6f646b63 bump version 2024-03-17 14:10:14 +01:00
m2049r
aa530caa28 improve german transaltion. remove permissions translations 2024-03-17 14:04:49 +01:00
m2049r
5df43f1274 fix translation errors 2024-03-17 13:38:36 +01:00
m2049r
3b0cb3ffdb upgrade gradle 2024-03-17 13:28:22 +01:00
m2049r
300551dbe4 update zlib to 1.3.1 2024-03-17 13:06:05 +01:00
m2049r
942519adb7 ignore monero native stuff 2024-03-17 12:53:51 +01:00
m2049r
0652a8ba14 upgrade dnsjava (#924) 2024-01-13 17:33:23 +01:00
m2049r
04d0bd2ffb fix tests 2024-01-13 16:14:05 +01:00
m2049r
8473e66c69 upgrade dependencies 2024-01-13 15:58:00 +01:00
m2049r
2218ff615c fix unescaped quotes 2024-01-13 15:41:23 +01:00
m2049r
54b55b9f8f optimize build time 2024-01-13 15:33:21 +01:00
m2049r
5abf84f62b optimize build time 2024-01-13 15:31:14 +01:00
m2049r
6ff75d221f add RSD (#923) 2024-01-13 14:28:30 +01:00
audioz
c90f107c3c Hebrew Translation (#895) 2024-01-13 14:13:56 +01:00
Digitale Souveränität - JETZT!
a3db18032e add ds-jetzt node (#905)
add ds-jetzt node as clear & onion
2024-01-13 14:13:18 +01:00
anhdres
9db2c10679 updated texts for eng and spa (#911)
Co-authored-by: m2049r <m2049r@monerujo.io>
2024-01-13 14:11:41 +01:00
XD22
eab85c7f0a Fix Docker libs Builds (#918)
Co-authored-by: m2049r <m2049r@monerujo.io>
2024-01-13 14:08:02 +01:00
kuznetsovgm
6bf3229e77 fixed a translation error (#912) 2024-01-13 14:05:52 +01:00
audioz
9f4f626acb Docker build fix. (#896)
* Hebrew Translation

* ~~ https://zlib.net/
Version 1.2.13 has these key updates from 1.2.12:

    Fix a bug when getting a gzip header extra field with inflateGetHeader(). This remedies CVE-2022-37434.
    Fix a bug in block type selection when Z_FIXED used. Now the smallest block type is selected, for better compression.
    Fix a configure issue that discarded the provided CC definition.
    Correct incorrect inputs provided to the CRC functions. This mitigates a bug in Java.
    Repair prototypes and exporting of the new CRC functions.
    Fix inflateBack to detect invalid input with distances too far.

Due to the first bug fix, any installations of 1.2.12 or earlier should be replaced with 1.2.13.

* Delete app/src/main/res/values-he directory
2024-01-13 14:05:22 +01:00
XD22
434dab55ba Fix TalkBack Screen reader (#917)
* Fixing TalkBack p1

* Fix TalkBack español translate p2

* Fix TalkBack français translate p3

* Escape some apostrophe, Fix wrong Locale codes
2024-01-13 14:03:31 +01:00
m2049r
59cc6b1864 upgrade dependencies 2024-01-13 13:59:38 +01:00
m2049r
a6e9d0e77c use yadio for pricing "exotic" fiat (#921)
* yadio api

* add more currencies

* fix es typo

* bump version & fix cicleci build
2023-12-10 14:24:35 +01:00
m2049r
17df7c3faf Pocket Change V2 (#914)
* show PC on btc confirm
* random slots to refill
2023-08-28 19:24:45 +02:00
m2049r
bf1829f775 PocketChange (#901) 2023-06-05 05:03:51 +02:00
m2049r
bc4aa0f772 Feature v0.18.2.2 (#900)
* add Ledger Stax

* update block heights

* reestimate restore height only for mainnet

* upgrade to gradle 8.0.1

* upgrade dependencies
2023-05-29 17:01:07 +02:00
108 changed files with 2982 additions and 2329 deletions

View File

@@ -3,7 +3,7 @@ jobs:
build:
working_directory: ~/code
docker:
- image: cimg/android:2022.03-ndk
- image: cimg/android:2023.12-ndk
environment:
JVM_OPTS: -Xmx3200m
steps:

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@
/app/prodStagenet
/app/.cxx
/monerujo.id
/external-libs/VERSION
/external-libs/include/wallet2_api.h

View File

@@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 33
buildToolsVersion '33.0.2'
ndkVersion '17.2.4988734'
defaultConfig {
applicationId "com.m2049r.xmrwallet"
buildToolsVersion = '34.0.0'
compileSdk 34
minSdkVersion 21
targetSdkVersion 31
versionCode 3130
versionName "3.1.3 'Fluorine Fermi'"
targetSdkVersion 33
versionCode 3311
versionName "3.3.11 'Argentina'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -112,6 +112,17 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
namespace 'com.m2049r.xmrwallet'
buildFeatures {
buildConfig true
}
testOptions {
unitTests {
all {
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
}
}
}
}
static def getId(name) {
@@ -123,39 +134,38 @@ static def getId(name) {
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
implementation 'androidx.core:core:1.10.0'
implementation 'androidx.core:core:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.preference:preference:1.2.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:4.9.3"
implementation "io.github.rburgst:okhttp-digest:2.6"
implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation "io.github.rburgst:okhttp-digest:3.1.0"
implementation "com.jakewharton.timber:timber:5.0.1"
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
implementation 'com.nulab-inc:zxcvbn:1.5.2'
implementation 'com.nulab-inc:zxcvbn:1.8.2'
implementation 'dnsjava:dnsjava:2.1.9'
implementation 'org.jitsi:dnssecjava:1.2.0'
implementation 'org.slf4j:slf4j-nop:1.7.36'
implementation 'dnsjava:dnsjava:3.5.3'
implementation 'org.slf4j:slf4j-nop:2.0.11'
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
//noinspection GradleDependency
testImplementation "junit:junit:4.13.2"
testImplementation "org.mockito:mockito-all:1.10.19"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
testImplementation 'org.json:json:20211205'
testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0"
testImplementation 'org.json:json:20231013'
testImplementation 'net.jodah:concurrentunit:0.4.6'
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
}

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.FingerprintHelper;
@@ -344,21 +345,23 @@ public class GenerateFragment extends Fragment {
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
if (restoreHeight.isEmpty()) return -1;
try {
// is it a date?
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
if ((height < 0) && (restoreHeight.length() == 8))
if (WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) {
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
// is it a date?
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
if ((height < 0) && (restoreHeight.length() == 8))
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
}
if (height < 0)
try {
// or is it a height?

View File

@@ -658,11 +658,11 @@ public class LoginActivity extends BaseActivity
break;
case NetworkType_Testnet:
toolbar.setSubtitle(getString(R.string.connect_testnet));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, androidx.appcompat.R.attr.colorPrimaryDark));
break;
case NetworkType_Stagenet:
toolbar.setSubtitle(getString(R.string.connect_stagenet));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, androidx.appcompat.R.attr.colorPrimaryDark));
break;
default:
throw new IllegalStateException("NetworkType unknown: " + net);

View File

@@ -79,6 +79,7 @@ public class TxFragment extends Fragment {
private TextView tvTxPaymentId;
private TextView tvTxBlockheight;
private TextView tvTxAmount;
private TextView tvTxPocketChangeAmount;
private TextView tvTxFee;
private TextView tvTxTransfers;
private TextView etTxNotes;
@@ -116,6 +117,7 @@ public class TxFragment extends Fragment {
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxPocketChangeAmount = view.findViewById(R.id.tvTxPocketChangeAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
etTxNotes = view.findViewById(R.id.etTxNotes);
@@ -249,8 +251,10 @@ public class TxFragment extends Fragment {
}
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
long realAmount = info.amount;
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
final long pcAmount = info.getPocketChangeAmount();
tvTxPocketChangeAmount.setVisibility(pcAmount > 0 ? View.VISIBLE : View.GONE);
tvTxPocketChangeAmount.setText(getString(R.string.pocketchange_tx_detail, Wallet.getDisplayAmount(pcAmount)));
if ((info.fee > 0)) {
String fee = Wallet.getDisplayAmount(info.fee);

View File

@@ -52,8 +52,8 @@ import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.dialog.PocketChangeFragment;
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
import com.m2049r.xmrwallet.fragment.send.SendFragment;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
@@ -82,7 +82,8 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
WalletFragment.DrawerLocker,
NavigationView.OnNavigationItemSelectedListener,
SubaddressFragment.Listener,
SubaddressInfoFragment.Listener {
SubaddressInfoFragment.Listener,
PocketChangeFragment.Listener {
public static final String REQUEST_ID = "id";
public static final String REQUEST_PW = "pw";
@@ -285,8 +286,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
onWalletRescan();
} else if (itemId == R.id.action_info) {
onWalletDetails();
} else if (itemId == R.id.action_credits) {
CreditsFragment.display(getSupportFragmentManager());
} else if (itemId == R.id.action_share) {
onShareTxInfo();
} else if (itemId == R.id.action_help_tx_info) {
@@ -301,6 +300,8 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
HelpFragment.display(getSupportFragmentManager(), R.string.help_send);
} else if (itemId == R.id.action_rename) {
onAccountRename();
} else if (itemId == R.id.action_pocketchange) {
PocketChangeFragment.display(getSupportFragmentManager(), getWallet().getPocketChangeSetting());
} else if (itemId == R.id.action_subaddresses) {
showSubaddresses(true);
} else if (itemId == R.id.action_streetmode) {
@@ -422,7 +423,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
break;
case NetworkType_Stagenet:
case NetworkType_Testnet:
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, androidx.appcompat.R.attr.colorPrimaryDark));
break;
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
@@ -646,6 +647,8 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
haveWallet = true;
invalidateOptionsMenu();
loadPocketChangeSettings();
if (requestStreetMode) onEnableStreetMode();
final WalletFragment walletFragment = getWalletFragment();
@@ -1104,6 +1107,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
public void setAccountIndex(int accountIndex) {
getWallet().setAccountIndex(accountIndex);
loadPocketChangeSettings();
selectedSubaddressIndex = 0;
}
@@ -1214,4 +1218,19 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
b.putInt("subaddressIndex", subaddressIndex);
replaceFragmentWithTransition(view, new SubaddressInfoFragment(), null, b);
}
@Override
public void setPocketChange(Wallet.PocketChangeSetting setting) {
SharedPreferences.Editor editor = getPrefs().edit();
editor.putString(getWallet().getAddress() + "_PC", setting.toPrefString());
editor.apply();
getWallet().setPocketChangeSetting(setting);
}
public void loadPocketChangeSettings() {
final String settings = getPrefs().getString(getWallet().getAddress() + "_PC", "0");
getWallet().setPocketChangeSetting(Wallet.PocketChangeSetting.from(settings));
}
}

View File

@@ -112,7 +112,7 @@ public class WalletFragment extends Fragment
flExchange = view.findViewById(R.id.flExchange);
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
setColorFilter(
ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
ThemeHelper.getThemedColor(getContext(), com.google.android.material.R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY);
tvProgress = view.findViewById(R.id.tvProgress);

View File

@@ -22,18 +22,24 @@ import android.content.res.Configuration;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStateManagerControl;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.util.LocaleHelper;
import com.m2049r.xmrwallet.util.NetCipherHelper;
import com.m2049r.xmrwallet.util.NightmodeHelper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import java.util.Arrays;
import timber.log.Timber;
public class XmrWalletApplication extends Application {
@Override
@OptIn(markerClass = FragmentStateManagerControl.class)
public void onCreate() {
super.onCreate();
FragmentManager.enableNewStateManager(false);

View File

@@ -29,10 +29,12 @@ public enum DefaultNodes {
HASHVAULT("nodes.hashvault.pro:18081"),
MONEROWORLD("node.moneroworld.com:18089"),
XMRTW("opennode.xmr-tw.org:18089"),
ds_jetzt("monero.ds-jetzt.de:18089"),
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion");
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion"),
ds_jetzt_onion("qvlr4w7yhnjrdg3txa72jwtpnjn4ezsrivzvocbnvpfbdo342fahhoad.onion:18089/mainnet/ds-jetzt.onion");
@Getter
private final String uri;

View File

@@ -270,7 +270,7 @@ public class NodeInfo extends Node {
(hostAddress.isOnion() ? "&nbsp;.onion&nbsp;&nbsp;" : ""), " " + info));
view.setText(text);
if (isError)
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorError));
view.setTextColor(ThemeHelper.getThemedColor(ctx, androidx.appcompat.R.attr.colorError));
else
view.setTextColor(ThemeHelper.getThemedColor(ctx, android.R.attr.textColorSecondary));
}

File diff suppressed because it is too large Load Diff

View File

@@ -41,10 +41,6 @@ public class TxDataBtc extends TxData {
super();
}
public TxDataBtc(TxDataBtc txDataBtc) {
super(txDataBtc);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);

View File

@@ -0,0 +1,133 @@
/*
* Copyright (c) 2023 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.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.slider.Slider;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
public class PocketChangeFragment extends DialogFragment implements Slider.OnChangeListener {
static final String TAG = "PocketChangeFragment";
static final String ENABLED = "enabled";
static final String TICK = "tick";
public static PocketChangeFragment newInstance(boolean enabled, int tick) {
PocketChangeFragment fragment = new PocketChangeFragment();
Bundle bundle = new Bundle();
bundle.putInt(ENABLED, enabled ? 1 : 0);
bundle.putInt(TICK, tick);
fragment.setArguments(bundle);
return fragment;
}
public static void display(FragmentManager fm, @NonNull Wallet.PocketChangeSetting setting) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(TAG);
if (prev != null) {
ft.remove(prev);
}
PocketChangeFragment.newInstance(setting.isEnabled(), getTick(setting.getAmount())).show(ft, TAG);
}
SwitchMaterial switchPocketChange;
Slider slider;
TextView tvProgressLabel;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pocketchange_setting, null);
boolean enabled = false;
int progress = 0;
Bundle arguments = getArguments();
if (arguments != null) {
enabled = arguments.getInt(ENABLED) > 0;
progress = arguments.getInt(TICK);
}
final View llAmount = view.findViewById(R.id.llAmount);
switchPocketChange = view.findViewById(R.id.switchPocketChange);
switchPocketChange.setOnCheckedChangeListener((buttonView, isChecked) -> llAmount.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE));
slider = view.findViewById(R.id.seekbar);
slider.addOnChangeListener(this);
switchPocketChange.setChecked(enabled);
tvProgressLabel = view.findViewById(R.id.seekbar_value);
slider.setValue(progress);
llAmount.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
onValueChange(slider, slider.getValue(), false);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity())
.setView(view)
.setPositiveButton(R.string.label_apply,
(dialog, whichButton) -> {
final FragmentActivity activity = getActivity();
if (activity instanceof Listener) {
((Listener) activity).setPocketChange(Wallet.PocketChangeSetting.of(switchPocketChange.isChecked(), getAmount()));
}
}
);
return builder.create();
}
private long getAmount() {
return Wallet.getAmountFromDouble(getAmount((int) slider.getValue()));
}
private static final double[] AMOUNTS = {0.1, 0.2, 0.3, 0.5, 0.8, 1.3};
private static double getAmount(int i) {
return AMOUNTS[i];
}
// find the closest amount we have
private static int getTick(long amount) {
int enabled = amount > 0 ? 1 : -1;
amount = Math.abs(amount);
double lastDiff = Double.MAX_VALUE;
for (int i = 0; i < AMOUNTS.length; i++) {
final double diff = Math.abs(Helper.ONE_XMR * AMOUNTS[i] - amount);
if (lastDiff < diff) return i - 1;
lastDiff = diff;
}
return enabled * (AMOUNTS.length - 1);
}
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
tvProgressLabel.setText(getString(R.string.pocketchange_amount, getAmount((int) value)));
}
public interface Listener {
void setPocketChange(Wallet.PocketChangeSetting setting);
}
}

View File

@@ -411,10 +411,10 @@ public class SendAddressWizardFragment extends SendWizardFragment {
if (txData instanceof TxDataBtc) {
((TxDataBtc) txData).setBtcAddress(etAddress.getEditText().getText().toString());
((TxDataBtc) txData).setBtcSymbol(selectedCrypto.getSymbol());
txData.setDestinationAddress(null);
txData.setDestination(null);
ServiceHelper.ASSET = selectedCrypto.getSymbol().toLowerCase();
} else {
txData.setDestinationAddress(etAddress.getEditText().getText().toString());
txData.setDestination(etAddress.getEditText().getText().toString());
ServiceHelper.ASSET = null;
}
txData.setUserNotes(new UserNotes(etNotes.getEditText().getText().toString()));

View File

@@ -23,6 +23,8 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxData;

View File

@@ -74,6 +74,9 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
private View llConfirmSend;
private Button bSend;
private View pbProgressSend;
private TextView tvTxChange;
private View llPocketChange;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -92,6 +95,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTotal = view.findViewById(R.id.tvTxTotal);
tvTxChange = view.findViewById(R.id.tvTxChange);
llPocketChange = view.findViewById(R.id.llPocketChange);
llStageA = view.findViewById(R.id.llStageA);
evStageA = view.findViewById(R.id.evStageA);
@@ -217,6 +222,13 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
tvTxTotal.setText(Wallet.getDisplayAmount(
pendingTransaction.getFee() + pendingTransaction.getAmount()));
final long change = pendingTransaction.getPocketChange();
if (change > 0) {
llPocketChange.setVisibility(View.VISIBLE);
tvTxChange.setText(Wallet.getDisplayAmount(change));
} else {
llPocketChange.setVisibility(View.GONE);
}
updateSendButton();
});
} else {
@@ -348,7 +360,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
}
showProgress(3, getString(R.string.label_send_progress_create_tx));
final TxData txData = sendListener.getTxData();
txData.setDestinationAddress(xmrtoOrder.getXmrAddress());
txData.setDestination(xmrtoOrder.getXmrAddress());
txData.setAmount(xmrtoOrder.getXmrAmount());
getActivityCallback().onPrepareSend(xmrtoOrder.getOrderId(), txData);
}

View File

@@ -140,7 +140,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
isResumed = true;
btcData = (TxDataBtc) sendListener.getTxData();
tvTxAddress.setText(btcData.getDestinationAddress());
tvTxAddress.setText(btcData.getDestination());
final PendingTx committedTx = sendListener.getCommittedTx();
if (committedTx != null) {

View File

@@ -64,12 +64,14 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
private TextView tvTxAddress;
private TextView tvTxNotes;
private TextView tvTxAmount;
private TextView tvTxChange;
private TextView tvTxFee;
private TextView tvTxTotal;
private View llProgress;
private View bSend;
private View llConfirmSend;
private View pbProgressSend;
private View llPocketChange;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -83,12 +85,14 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxNotes = view.findViewById(R.id.tvTxNotes);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxChange = view.findViewById(R.id.tvTxChange);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTotal = view.findViewById(R.id.tvTxTotal);
llProgress = view.findViewById(R.id.llProgress);
pbProgressSend = view.findViewById(R.id.pbProgressSend);
llConfirmSend = view.findViewById(R.id.llConfirmSend);
llPocketChange = view.findViewById(R.id.llPocketChange);
bSend = view.findViewById(R.id.bSend);
bSend.setEnabled(false);
@@ -181,7 +185,7 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
isResumed = true;
final TxData txData = sendListener.getTxData();
tvTxAddress.setText(txData.getDestinationAddress());
tvTxAddress.setText(txData.getDestination());
UserNotes notes = sendListener.getTxData().getUserNotes();
if ((notes != null) && (!notes.note.isEmpty())) {
tvTxNotes.setText(notes.note);
@@ -206,7 +210,14 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
tvTxAmount.setText(getString(R.string.street_sweep_amount));
tvTxTotal.setText(getString(R.string.street_sweep_amount));
} else {
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount()));
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getNetAmount()));
final long change = pendingTransaction.getPocketChange();
if (change > 0) {
llPocketChange.setVisibility(View.VISIBLE);
tvTxChange.setText(Wallet.getDisplayAmount(change));
} else {
llPocketChange.setVisibility(View.GONE);
}
tvTxTotal.setText(Wallet.getDisplayAmount(
pendingTransaction.getFee() + pendingTransaction.getAmount()));
}

View File

@@ -108,7 +108,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
Helper.hideKeyboard(getActivity());
final TxData txData = sendListener.getTxData();
tvTxAddress.setText(txData.getDestinationAddress());
tvTxAddress.setText(txData.getDestination());
final PendingTx committedTx = sendListener.getCommittedTx();
if (committedTx != null) {

View File

@@ -189,7 +189,6 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
}
private void showLock() {
Timber.d("UNLOCK %d:%d", infoItem.unlockTime, infoItem.blockheight);
if (infoItem.unlockTime == 0) {
ivLock.setVisibility(View.GONE);
return;
@@ -220,7 +219,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ivTxType.setVisibility(View.GONE);
}
String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
String displayAmount = Helper.getDisplayAmount(infoItem.getNetAmount(), Helper.DISPLAY_DIGITS_INFO);
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
} else {

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 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.model;
import java.util.List;
import timber.log.Timber;
public class Coins {
static {
System.loadLibrary("monerujo");
}
private long handle;
public Coins(long handle) {
this.handle = handle;
}
public List<CoinsInfo> getAll(int accountIndex, boolean unspentOnly) {
return refresh(accountIndex, unspentOnly);
}
private native List<CoinsInfo> refresh(int accountIndex, boolean unspentOnly);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 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.model;
import lombok.Value;
// this is not the CoinsInfo from the API as that is owned by the Coins object
// this is a POJO
@Value
public class CoinsInfo {
int accountIndex;
int addressIndex;
long amount;
long blockheight;
String txHash;
boolean spent;
boolean frozen;
long unlockTime;
boolean unlocked;
public boolean isSpendable() {
return !spent && unlocked;
}
}

View File

@@ -16,6 +16,9 @@
package com.m2049r.xmrwallet.model;
import lombok.Getter;
import lombok.Setter;
public class PendingTransaction {
static {
System.loadLibrary("monerujo");
@@ -63,8 +66,6 @@ public class PendingTransaction {
Priority(int value) {
this.value = value;
}
}
public Status getStatus() {
@@ -95,4 +96,11 @@ public class PendingTransaction {
public native long getTxCount();
@Getter
@Setter
private long pocketChange;
public long getNetAmount() {
return getAmount() - pocketChange;
}
}

View File

@@ -17,8 +17,10 @@
package com.m2049r.xmrwallet.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import timber.log.Timber;
@@ -61,22 +63,14 @@ public class TransactionHistory {
private List<TransactionInfo> transactions = new ArrayList<>();
void refreshWithNotes(Wallet wallet) {
public void refreshWithNotes(Wallet wallet) {
refresh();
loadNotes(wallet);
}
private void refresh() {
List<TransactionInfo> transactionInfos = refreshJ();
Timber.d("refresh size=%d", transactionInfos.size());
for (Iterator<TransactionInfo> iterator = transactionInfos.iterator(); iterator.hasNext(); ) {
TransactionInfo info = iterator.next();
if (info.accountIndex != accountIndex) {
iterator.remove();
}
}
transactions = transactionInfos;
transactions = refreshJ(accountIndex);
}
private native List<TransactionInfo> refreshJ();
private native List<TransactionInfo> refreshJ(int accountIndex);
}

View File

@@ -101,6 +101,23 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
this.unlockTime = unlockTime;
this.subaddressLabel = subaddressLabel;
this.transfers = transfers;
calcNetAmount();
}
@Getter
private long netAmount;
public long getPocketChangeAmount() {
return amount - netAmount;
}
private void calcNetAmount() {
netAmount = amount;
if ((direction == TransactionInfo.Direction.Direction_Out) && (transfers != null)) {
for (int i = 1; i < transfers.size(); i++) {
netAmount -= transfers.get(i).amount;
}
}
}
public boolean isConfirmed() {
@@ -138,6 +155,7 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(txKey);
out.writeString(notes);
out.writeString(address);
out.writeLong(netAmount);
}
public static final Parcelable.Creator<TransactionInfo> CREATOR = new Parcelable.Creator<TransactionInfo>() {
@@ -169,6 +187,7 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
txKey = in.readString();
notes = in.readString();
address = in.readString();
netAmount = in.readLong();
}
@Override

View File

@@ -25,10 +25,13 @@ import com.m2049r.xmrwallet.data.TxData;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.Value;
import timber.log.Timber;
public class Wallet {
@@ -67,9 +70,7 @@ public class Wallet {
}
public boolean isOk() {
return (getStatus() == StatusEnum.Status_Ok)
&& ((getConnectionStatus() == null) ||
(getConnectionStatus() == ConnectionStatus.ConnectionStatus_Connected));
return (getStatus() == StatusEnum.Status_Ok) && ((getConnectionStatus() == null) || (getConnectionStatus() == ConnectionStatus.ConnectionStatus_Connected));
}
@Override
@@ -110,23 +111,17 @@ public class Wallet {
@RequiredArgsConstructor
@Getter
public enum Device {
Device_Undefined(0, 0),
Device_Software(50, 200),
Device_Ledger(5, 20);
Device_Undefined(0, 0), Device_Software(50, 200), Device_Ledger(5, 20);
private final int accountLookahead;
private final int subaddressLookahead;
}
public enum StatusEnum {
Status_Ok,
Status_Error,
Status_Critical
Status_Ok, Status_Error, Status_Critical
}
public enum ConnectionStatus {
ConnectionStatus_Disconnected,
ConnectionStatus_Connected,
ConnectionStatus_WrongVersion
ConnectionStatus_Disconnected, ConnectionStatus_Connected, ConnectionStatus_WrongVersion
}
public native String getSeed(String offset);
@@ -168,16 +163,14 @@ public class Wallet {
private native String getAddressJ(int accountIndex, int addressIndex);
public Subaddress getSubaddressObject(int accountIndex, int subAddressIndex) {
return new Subaddress(accountIndex, subAddressIndex,
getSubaddress(subAddressIndex), getSubaddressLabel(subAddressIndex));
return new Subaddress(accountIndex, subAddressIndex, getSubaddress(subAddressIndex), getSubaddressLabel(subAddressIndex));
}
public Subaddress getSubaddressObject(int subAddressIndex) {
Subaddress subaddress = getSubaddressObject(accountIndex, subAddressIndex);
long amount = 0;
for (TransactionInfo info : getHistory().getAll()) {
if ((info.addressIndex == subAddressIndex)
&& (info.direction == TransactionInfo.Direction.Direction_In)) {
if ((info.addressIndex == subAddressIndex) && (info.direction == TransactionInfo.Direction.Direction_In)) {
amount += info.amount;
}
}
@@ -217,13 +210,10 @@ public class Wallet {
// virtual std::string keysFilename() const = 0;
public boolean init(long upper_transaction_size_limit) {
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit,
WalletManager.getInstance().getDaemonUsername(),
WalletManager.getInstance().getDaemonPassword());
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit, WalletManager.getInstance().getDaemonUsername(), WalletManager.getInstance().getDaemonPassword());
}
private native boolean initJ(String daemon_address, long upper_transaction_size_limit,
String daemon_username, String daemon_password);
private native boolean initJ(String daemon_address, long upper_transaction_size_limit, String daemon_username, String daemon_password);
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
@@ -335,36 +325,23 @@ public class Wallet {
}
}
public PendingTransaction createTransaction(TxData txData) {
return createTransaction(
txData.getDestinationAddress(),
txData.getAmount(),
txData.getMixin(),
txData.getPriority());
}
private native long createTransactionMultDest(String[] destinations, String payment_id, long[] amounts, int mixin_count, int priority, int accountIndex, int[] subaddresses);
public PendingTransaction createTransaction(String dst_addr,
long amount, int mixin_count,
PendingTransaction.Priority priority) {
public PendingTransaction createTransaction(TxData txData) {
disposePendingTransaction();
int _priority = priority.getValue();
long txHandle =
(amount == SWEEP_ALL ?
createSweepTransaction(dst_addr, "", mixin_count, _priority,
accountIndex) :
createTransactionJ(dst_addr, "", amount, mixin_count, _priority,
accountIndex));
int _priority = txData.getPriority().getValue();
final boolean sweepAll = txData.getAmount() == SWEEP_ALL;
Timber.d("TxData: %s", txData);
long txHandle = (sweepAll ? createSweepTransaction(txData.getDestination(), "", txData.getMixin(), _priority, accountIndex) :
createTransactionMultDest(txData.getDestinations(), "", txData.getAmounts(), txData.getMixin(), _priority, accountIndex, txData.getSubaddresses()));
pendingTransaction = new PendingTransaction(txHandle);
pendingTransaction.setPocketChange(txData.getPocketChangeAmount());
return pendingTransaction;
}
private native long createTransactionJ(String dst_addr, String payment_id,
long amount, int mixin_count,
int priority, int accountIndex);
private native long createTransactionJ(String dst_addr, String payment_id, long amount, int mixin_count, int priority, int accountIndex);
private native long createSweepTransaction(String dst_addr, String payment_id,
int mixin_count,
int priority, int accountIndex);
private native long createSweepTransaction(String dst_addr, String payment_id, int mixin_count, int priority, int accountIndex);
public PendingTransaction createSweepUnmixableTransaction() {
@@ -381,7 +358,13 @@ public class Wallet {
public native void disposeTransaction(PendingTransaction pendingTransaction);
//virtual bool exportKeyImages(const std::string &filename) = 0;
public long estimateTransactionFee(TxData txData) {
return estimateTransactionFee(txData.getDestinations(), txData.getAmounts(), txData.getPriority().getValue());
}
private native long estimateTransactionFee(String[] destinations, long[] amounts, int priority);
//virtual bool exportKeyImages(const std::string &filename) = 0;
//virtual bool importKeyImages(const std::string &filename) = 0;
@@ -403,6 +386,22 @@ public class Wallet {
}
//virtual AddressBook * addressBook() const = 0;
public List<CoinsInfo> getCoinsInfos(boolean unspentOnly) {
return getCoins().getAll(accountIndex, unspentOnly);
}
private Coins coins = null;
private Coins getCoins() {
if (coins == null) {
coins = new Coins(getCoinsJ());
}
return coins;
}
private native long getCoinsJ();
//virtual void setListener(WalletListener *) = 0;
private native long setListenerJ(WalletListener listener);
@@ -444,8 +443,7 @@ public class Wallet {
if (label.equals(NEW_ACCOUNT_NAME)) {
String address = getAddress(accountIndex);
int len = address.length();
label = address.substring(0, 6) +
"\u2026" + address.substring(len - 6, len);
label = address.substring(0, 6) + "\u2026" + address.substring(len - 6, len);
}
return label;
}
@@ -504,4 +502,22 @@ public class Wallet {
private native int getDeviceTypeJ();
@Getter
@Setter
PocketChangeSetting pocketChangeSetting = PocketChangeSetting.of(false, 0);
@Value(staticConstructor = "of")
static public class PocketChangeSetting {
boolean enabled;
long amount;
public String toPrefString() {
return Long.toString((enabled ? 1 : -1) * amount);
}
static public PocketChangeSetting from(String prefString) {
long value = Long.parseLong(prefString);
return of(value > 0, Math.abs(value));
}
}
}

View File

@@ -93,7 +93,7 @@ public class WalletManager {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet);
if (wallet.getStatus().isOk()) {
if (wallet.getStatus().isOk() && (wallet.getNetworkType() == NetworkType.NetworkType_Mainnet)) {
// (Re-)Estimate restore height based on what we know
final long oldHeight = wallet.getRestoreHeight();
// Go back 4 days if we don't have a precise restore height

View File

@@ -318,9 +318,10 @@ public class WalletService extends Service {
TxData txData = extras.getParcelable(REQUEST_CMD_TX_DATA);
String txTag = extras.getString(REQUEST_CMD_TX_TAG);
assert txData != null;
txData.createPocketChange(myWallet);
PendingTransaction pendingTransaction = myWallet.createTransaction(txData);
PendingTransaction.Status status = pendingTransaction.getStatus();
Timber.d("transaction status %s", status);
if (status != PendingTransaction.Status.Status_Ok) {
Timber.w("Create Transaction failed: %s", pendingTransaction.getErrorString());
}

View File

@@ -33,5 +33,6 @@ public interface ExchangeApi {
void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
@NonNull final ExchangeCallback callback);
String getName();
}

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