PocketChange (#901)

This commit is contained in:
m2049r 2023-06-05 05:03:51 +02:00 committed by GitHub
parent bc4aa0f772
commit bf1829f775
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1179 additions and 320 deletions

View File

@ -8,8 +8,8 @@ android {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 3200 versionCode 3307
versionName "3.2.0 'Decoy Selection'" versionName "3.3.7 'Pocket Change'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {

View File

@ -34,8 +34,10 @@ extern "C"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__)
static JavaVM *cachedJVM; static JavaVM *cachedJVM;
static jclass class_String;
static jclass class_ArrayList; static jclass class_ArrayList;
static jclass class_WalletListener; static jclass class_WalletListener;
static jclass class_CoinsInfo;
static jclass class_TransactionInfo; static jclass class_TransactionInfo;
static jclass class_Transfer; static jclass class_Transfer;
static jclass class_Ledger; static jclass class_Ledger;
@ -43,6 +45,14 @@ static jclass class_WalletStatus;
std::mutex _listenerMutex; std::mutex _listenerMutex;
//void jstringToString(JNIEnv *env, std::string &str, jstring jstr) {
// if (!jstr) return;
// const int len = env->GetStringUTFLength(jstr);
// const char *chars = env->GetStringUTFChars(jstr, nullptr);
// str.assign(chars, len);
// env->ReleaseStringUTFChars(jstr, chars);
//}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm; cachedJVM = jvm;
LOGI("JNI_OnLoad"); LOGI("JNI_OnLoad");
@ -52,8 +62,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
} }
//LOGI("JNI_OnLoad ok"); //LOGI("JNI_OnLoad ok");
class_String = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("java/lang/String")));
class_ArrayList = static_cast<jclass>(jenv->NewGlobalRef( class_ArrayList = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("java/util/ArrayList"))); jenv->FindClass("java/util/ArrayList")));
class_CoinsInfo = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/CoinsInfo")));
class_TransactionInfo = static_cast<jclass>(jenv->NewGlobalRef( class_TransactionInfo = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo"))); jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo")));
class_Transfer = static_cast<jclass>(jenv->NewGlobalRef( class_Transfer = static_cast<jclass>(jenv->NewGlobalRef(
@ -951,6 +965,58 @@ Java_com_m2049r_xmrwallet_model_Wallet_rescanBlockchainAsyncJ(JNIEnv *env, jobje
//TODO virtual void setAutoRefreshInterval(int millis) = 0; //TODO virtual void setAutoRefreshInterval(int millis) = 0;
//TODO virtual int autoRefreshInterval() const = 0; //TODO virtual int autoRefreshInterval() const = 0;
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_createTransactionMultDest(JNIEnv *env, jobject instance,
jobjectArray destinations,
jstring payment_id,
jlongArray amounts,
jint mixin_count,
jint priority,
jint accountIndex,
jintArray subaddresses) {
std::vector<std::string> dst_addr;
std::vector<uint64_t> amount;
int destSize = env->GetArrayLength(destinations);
assert(destSize == env->GetArrayLength(amounts));
jlong *_amounts = env->GetLongArrayElements(amounts, nullptr);
for (int i = 0; i < destSize; i++) {
jstring dest = (jstring) env->GetObjectArrayElement(destinations, i);
const char *_dest = env->GetStringUTFChars(dest, nullptr);
dst_addr.emplace_back(_dest);
env->ReleaseStringUTFChars(dest, _dest);
amount.emplace_back((uint64_t) _amounts[i]);
}
env->ReleaseLongArrayElements(amounts, _amounts, 0);
std::set<uint32_t> subaddr_indices;
if (subaddresses != nullptr) {
int subaddrSize = env->GetArrayLength(subaddresses);
jint *_subaddresses = env->GetIntArrayElements(subaddresses, nullptr);
for (int i = 0; i < subaddrSize; i++) {
subaddr_indices.insert((uint32_t) _subaddresses[i]);
}
env->ReleaseIntArrayElements(subaddresses, _subaddresses, 0);
}
const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr);
Monero::PendingTransaction::Priority _priority =
static_cast<Monero::PendingTransaction::Priority>(priority);
Monero::Wallet *wallet = getHandle<Monero::Wallet>(env, instance);
Monero::PendingTransaction *tx =
wallet->createTransactionMultDest(dst_addr, _payment_id,
amount, (uint32_t) mixin_count,
_priority,
(uint32_t) accountIndex,
subaddr_indices);
env->ReleaseStringUTFChars(payment_id, _payment_id);
return reinterpret_cast<jlong>(tx);
}
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_createTransactionJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_Wallet_createTransactionJ(JNIEnv *env, jobject instance,
jstring dst_addr, jstring payment_id, jstring dst_addr, jstring payment_id,
@ -1019,6 +1085,36 @@ Java_com_m2049r_xmrwallet_model_Wallet_disposeTransaction(JNIEnv *env, jobject i
wallet->disposeTransaction(_pendingTransaction); wallet->disposeTransaction(_pendingTransaction);
} }
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_estimateTransactionFee(JNIEnv *env, jobject instance,
jobjectArray addresses,
jlongArray amounts,
jint priority) {
std::vector<std::pair<std::string, uint64_t>> destinations;
int destSize = env->GetArrayLength(addresses);
assert(destSize == env->GetArrayLength(amounts));
jlong *_amounts = env->GetLongArrayElements(amounts, nullptr);
for (int i = 0; i < destSize; i++) {
std::pair<std::string, uint64_t> pair;
jstring dest = (jstring) env->GetObjectArrayElement(addresses, i);
const char *_dest = env->GetStringUTFChars(dest, nullptr);
pair.first = _dest;
env->ReleaseStringUTFChars(dest, _dest);
pair.second = ((uint64_t) _amounts[i]);
destinations.emplace_back(pair);
}
env->ReleaseLongArrayElements(amounts, _amounts, 0);
Monero::PendingTransaction::Priority _priority =
static_cast<Monero::PendingTransaction::Priority>(priority);
Monero::Wallet *wallet = getHandle<Monero::Wallet>(env, instance);
return static_cast<jlong>(wallet->estimateTransactionFee(destinations, _priority));
}
//virtual bool exportKeyImages(const std::string &filename) = 0; //virtual bool exportKeyImages(const std::string &filename) = 0;
//virtual bool importKeyImages(const std::string &filename) = 0; //virtual bool importKeyImages(const std::string &filename) = 0;
@ -1032,6 +1128,12 @@ Java_com_m2049r_xmrwallet_model_Wallet_getHistoryJ(JNIEnv *env, jobject instance
//virtual AddressBook * addressBook() const = 0; //virtual AddressBook * addressBook() const = 0;
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getCoinsJ(JNIEnv *env, jobject instance) {
Monero::Wallet *wallet = getHandle<Monero::Wallet>(env, instance);
return reinterpret_cast<jlong>(wallet->coins());
}
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_setListenerJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_Wallet_setListenerJ(JNIEnv *env, jobject instance,
jobject javaListener) { jobject javaListener) {
@ -1270,7 +1372,62 @@ jobject newTransactionInfo(JNIEnv *env, Monero::TransactionInfo *info) {
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
jobject cpp2java(JNIEnv *env, const std::vector<Monero::TransactionInfo *> &vector) { // Coins
jobject newCoinsInfo(JNIEnv *env, Monero::CoinsInfo *info) {
jstring _hash = env->NewStringUTF(info->hash().c_str());
jmethodID c = env->GetMethodID(class_CoinsInfo, "<init>", "(IIJJLjava/lang/String;ZZJZ)V");
jobject result = env->NewObject(class_CoinsInfo, c,
static_cast<jint> (info->subaddrAccount()),
static_cast<jint> (info->subaddrIndex()),
static_cast<jlong> (info->amount()),
static_cast<jlong> (info->blockHeight()),
_hash,
info->spent(),
info->frozen(),
static_cast<jlong> (info->unlockTime()),
info->unlocked());
env->DeleteLocalRef(_hash);
return result;
}
jobject coinsInfoArrayList(JNIEnv *env, const std::vector<Monero::CoinsInfo *> &vector,
uint32_t accountIndex, bool unspentOnly) {
jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
"(Ljava/lang/Object;)Z");
jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_,
static_cast<jint> (vector.size()));
for (Monero::CoinsInfo *s: vector) {
if (s->subaddrAccount() != accountIndex) continue;
if (s->spent() && unspentOnly) continue;
jobject info = newCoinsInfo(env, s);
env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info);
env->DeleteLocalRef(info);
}
return arrayList;
}
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_Coins_getCount(JNIEnv *env, jobject instance) {
Monero::Coins *coins = getHandle<Monero::Coins>(env, instance);
return coins->count();
}
JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_Coins_refresh(JNIEnv *env, jobject instance, jint accountIndex,
jboolean unspentOnly) {
Monero::Coins *coins = getHandle<Monero::Coins>(env, instance);
coins->refresh();
return coinsInfoArrayList(env, coins->getAll(), (uint32_t) accountIndex, unspentOnly);
}
jobject
transactionInfoArrayList(JNIEnv *env, const std::vector<Monero::TransactionInfo *> &vector,
uint32_t accountIndex) {
jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V"); jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
@ -1279,6 +1436,7 @@ jobject cpp2java(JNIEnv *env, const std::vector<Monero::TransactionInfo *> &vect
jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_,
static_cast<jint> (vector.size())); static_cast<jint> (vector.size()));
for (Monero::TransactionInfo *s: vector) { for (Monero::TransactionInfo *s: vector) {
if (s->subaddrAccount() != accountIndex) continue;
jobject info = newTransactionInfo(env, s); jobject info = newTransactionInfo(env, s);
env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info); env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info);
env->DeleteLocalRef(info); env->DeleteLocalRef(info);
@ -1287,11 +1445,12 @@ jobject cpp2java(JNIEnv *env, const std::vector<Monero::TransactionInfo *> &vect
} }
JNIEXPORT jobject JNICALL JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_refreshJ(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_TransactionHistory_refreshJ(JNIEnv *env, jobject instance,
jint accountIndex) {
Monero::TransactionHistory *history = getHandle<Monero::TransactionHistory>(env, Monero::TransactionHistory *history = getHandle<Monero::TransactionHistory>(env,
instance); instance);
history->refresh(); history->refresh();
return cpp2java(env, history->getAll()); return transactionInfoArrayList(env, history->getAll(), (uint32_t) accountIndex);
} }
// TransactionInfo is implemented in Java - no need here // TransactionInfo is implemented in Java - no need here
@ -1326,19 +1485,19 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_commit(JNIEnv *env, jobject i
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getAmount(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_PendingTransaction_getAmount(JNIEnv *env, jobject instance) {
Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance); Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance);
return tx->amount(); return static_cast<jlong>(tx->amount());
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getDust(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_PendingTransaction_getDust(JNIEnv *env, jobject instance) {
Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance); Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance);
return tx->dust(); return static_cast<jlong>(tx->dust());
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject instance) {
Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance); Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance);
return tx->fee(); return static_cast<jlong>(tx->fee());
} }
// TODO this returns a vector of strings - deal with this later - for now return first one // TODO this returns a vector of strings - deal with this later - for now return first one
@ -1355,7 +1514,7 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxIdJ(JNIEnv *env, jo
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobject instance) {
Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance); Monero::PendingTransaction *tx = getHandle<Monero::PendingTransaction>(env, instance);
return tx->txCount(); return static_cast<jlong>(tx->txCount());
} }

View File

@ -658,11 +658,11 @@ public class LoginActivity extends BaseActivity
break; break;
case NetworkType_Testnet: case NetworkType_Testnet:
toolbar.setSubtitle(getString(R.string.connect_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; break;
case NetworkType_Stagenet: case NetworkType_Stagenet:
toolbar.setSubtitle(getString(R.string.connect_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; break;
default: default:
throw new IllegalStateException("NetworkType unknown: " + net); throw new IllegalStateException("NetworkType unknown: " + net);

View File

@ -79,6 +79,7 @@ public class TxFragment extends Fragment {
private TextView tvTxPaymentId; private TextView tvTxPaymentId;
private TextView tvTxBlockheight; private TextView tvTxBlockheight;
private TextView tvTxAmount; private TextView tvTxAmount;
private TextView tvTxPocketChangeAmount;
private TextView tvTxFee; private TextView tvTxFee;
private TextView tvTxTransfers; private TextView tvTxTransfers;
private TextView etTxNotes; private TextView etTxNotes;
@ -116,6 +117,7 @@ public class TxFragment extends Fragment {
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId); tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight); tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
tvTxAmount = view.findViewById(R.id.tvTxAmount); tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxPocketChangeAmount = view.findViewById(R.id.tvTxPocketChangeAmount);
tvTxFee = view.findViewById(R.id.tvTxFee); tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTransfers = view.findViewById(R.id.tvTxTransfers); tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
etTxNotes = view.findViewById(R.id.etTxNotes); etTxNotes = view.findViewById(R.id.etTxNotes);
@ -249,8 +251,10 @@ public class TxFragment extends Fragment {
} }
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-"); String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
long realAmount = info.amount; tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount)); 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)) { if ((info.fee > 0)) {
String fee = Wallet.getDisplayAmount(info.fee); 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.Subaddress;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.UserNotes; import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; 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.SendAddressWizardFragment;
import com.m2049r.xmrwallet.fragment.send.SendFragment; import com.m2049r.xmrwallet.fragment.send.SendFragment;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog; import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
@ -82,7 +82,8 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
WalletFragment.DrawerLocker, WalletFragment.DrawerLocker,
NavigationView.OnNavigationItemSelectedListener, NavigationView.OnNavigationItemSelectedListener,
SubaddressFragment.Listener, SubaddressFragment.Listener,
SubaddressInfoFragment.Listener { SubaddressInfoFragment.Listener,
PocketChangeFragment.Listener {
public static final String REQUEST_ID = "id"; public static final String REQUEST_ID = "id";
public static final String REQUEST_PW = "pw"; public static final String REQUEST_PW = "pw";
@ -285,8 +286,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
onWalletRescan(); onWalletRescan();
} else if (itemId == R.id.action_info) { } else if (itemId == R.id.action_info) {
onWalletDetails(); onWalletDetails();
} else if (itemId == R.id.action_credits) {
CreditsFragment.display(getSupportFragmentManager());
} else if (itemId == R.id.action_share) { } else if (itemId == R.id.action_share) {
onShareTxInfo(); onShareTxInfo();
} else if (itemId == R.id.action_help_tx_info) { } 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); HelpFragment.display(getSupportFragmentManager(), R.string.help_send);
} else if (itemId == R.id.action_rename) { } else if (itemId == R.id.action_rename) {
onAccountRename(); onAccountRename();
} else if (itemId == R.id.action_pocketchange) {
PocketChangeFragment.display(getSupportFragmentManager(), getWallet().getPocketChangeSetting());
} else if (itemId == R.id.action_subaddresses) { } else if (itemId == R.id.action_subaddresses) {
showSubaddresses(true); showSubaddresses(true);
} else if (itemId == R.id.action_streetmode) { } else if (itemId == R.id.action_streetmode) {
@ -422,7 +423,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
break; break;
case NetworkType_Stagenet: case NetworkType_Stagenet:
case NetworkType_Testnet: case NetworkType_Testnet:
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark)); toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, androidx.appcompat.R.attr.colorPrimaryDark));
break; break;
default: default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType()); throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
@ -628,6 +629,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
public void onWalletStarted(final Wallet.Status walletStatus) { public void onWalletStarted(final Wallet.Status walletStatus) {
loadPocketChangeSettings();
runOnUiThread(() -> { runOnUiThread(() -> {
dismissProgressDialog(); dismissProgressDialog();
if (walletStatus == null) { if (walletStatus == null) {
@ -1104,6 +1106,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
public void setAccountIndex(int accountIndex) { public void setAccountIndex(int accountIndex) {
getWallet().setAccountIndex(accountIndex); getWallet().setAccountIndex(accountIndex);
loadPocketChangeSettings();
selectedSubaddressIndex = 0; selectedSubaddressIndex = 0;
} }
@ -1214,4 +1217,19 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
b.putInt("subaddressIndex", subaddressIndex); b.putInt("subaddressIndex", subaddressIndex);
replaceFragmentWithTransition(view, new SubaddressInfoFragment(), null, b); 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); flExchange = view.findViewById(R.id.flExchange);
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable(). ((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
setColorFilter( setColorFilter(
ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant), ThemeHelper.getThemedColor(getContext(), com.google.android.material.R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY); android.graphics.PorterDuff.Mode.MULTIPLY);
tvProgress = view.findViewById(R.id.tvProgress); tvProgress = view.findViewById(R.id.tvProgress);

View File

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

View File

@ -19,92 +19,88 @@ package com.m2049r.xmrwallet.data;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.m2049r.xmrwallet.model.CoinsInfo;
import com.m2049r.xmrwallet.model.PendingTransaction; import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import timber.log.Timber;
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents // https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
@ToString
public class TxData implements Parcelable { public class TxData implements Parcelable {
@Getter
private String[] destinations = new String[1];
@Getter
private long[] amounts = new long[1];
@Getter
@Setter
private int mixin;
@Getter
@Setter
private PendingTransaction.Priority priority;
@Getter
private int[] subaddresses;
@Getter
@Setter
private UserNotes userNotes;
public TxData() { public TxData() {
} }
public TxData(TxData txData) { public String getDestination() {
this.dstAddr = txData.dstAddr; return destinations[0];
this.amount = txData.amount;
this.mixin = txData.mixin;
this.priority = txData.priority;
}
public TxData(String dstAddr,
long amount,
int mixin,
PendingTransaction.Priority priority) {
this.dstAddr = dstAddr;
this.amount = amount;
this.mixin = mixin;
this.priority = priority;
}
public String getDestinationAddress() {
return dstAddr;
} }
public long getAmount() { public long getAmount() {
return amount; return amounts[0];
} }
public double getAmountAsDouble() { public long getPocketChangeAmount() {
return 1.0 * amount / Helper.ONE_XMR; long change = 0;
for (int i = 1; i < amounts.length; i++) {
change += amounts[i];
}
return change;
} }
public int getMixin() { public void setDestination(String destination) {
return mixin; destinations[0] = destination;
}
public PendingTransaction.Priority getPriority() {
return priority;
}
public void setDestinationAddress(String dstAddr) {
this.dstAddr = dstAddr;
} }
public void setAmount(long amount) { public void setAmount(long amount) {
this.amount = amount; amounts[0] = amount;
} }
public void setAmount(double amount) { public void setAmount(double amount) {
this.amount = Wallet.getAmountFromDouble(amount); setAmount(Wallet.getAmountFromDouble(amount));
} }
public void setMixin(int mixin) { private void resetPocketChange() {
this.mixin = mixin; if (destinations.length > 1) {
final String destination = getDestination();
destinations = new String[1];
destinations[0] = destination;
final long amount = getAmount();
amounts = new long[1];
amounts[0] = amount;
} }
public void setPriority(PendingTransaction.Priority priority) {
this.priority = priority;
} }
public UserNotes getUserNotes() {
return userNotes;
}
public void setUserNotes(UserNotes userNotes) {
this.userNotes = userNotes;
}
private String dstAddr;
private long amount;
private int mixin;
private PendingTransaction.Priority priority;
private UserNotes userNotes;
@Override @Override
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
out.writeString(dstAddr); out.writeInt(destinations.length);
out.writeLong(amount); out.writeStringArray(destinations);
out.writeLongArray(amounts);
out.writeInt(mixin); out.writeInt(mixin);
out.writeInt(priority.getValue()); out.writeInt(priority.getValue());
} }
@ -121,11 +117,13 @@ public class TxData implements Parcelable {
}; };
protected TxData(Parcel in) { protected TxData(Parcel in) {
dstAddr = in.readString(); int len = in.readInt();
amount = in.readLong(); destinations = new String[len];
in.readStringArray(destinations);
amounts = new long[len];
in.readLongArray(amounts);
mixin = in.readInt(); mixin = in.readInt();
priority = PendingTransaction.Priority.fromInteger(in.readInt()); priority = PendingTransaction.Priority.fromInteger(in.readInt());
} }
@Override @Override
@ -133,17 +131,117 @@ public class TxData implements Parcelable {
return 0; return 0;
} }
@Override //////////////////////////
public String toString() { /// PocketChange Stuff ///
StringBuffer sb = new StringBuffer(); //////////////////////////
sb.append("dstAddr:");
sb.append(dstAddr); final static public int POCKETCHANGE_IDX = 1; // subaddress index of first pocketchange slot
sb.append(",amount:"); final static public int POCKETCHANGE_SLOTS = 10; // number of pocketchange slots
sb.append(amount); final static public int POCKETCHANGE_IDX_MAX = POCKETCHANGE_IDX + POCKETCHANGE_SLOTS - 1;
sb.append(",mixin:");
sb.append(mixin); @Data
sb.append(",priority:"); static private class PocketChangeSlot {
sb.append(priority); private long amount;
return sb.toString(); private long spendableAmount;
public void add(CoinsInfo coin) {
amount += coin.getAmount();
if (coin.isSpendable()) spendableAmount += coin.getAmount();
}
}
// returns null if it can't create a PocketChange Transaction
// it assumes there is enough reserve to deal with fees - otherwise we get an error on
// creating the actual transaction
// String destination, long amount are already set!
public void createPocketChange(Wallet wallet) {
Wallet.PocketChangeSetting setting = wallet.getPocketChangeSetting();
if (!setting.isEnabled()) {
resetPocketChange();
return;
}
if ((destinations.length != 1) || (destinations[0] == null))
throw new IllegalStateException("invalid destinations");
if ((amounts.length != 1))
throw new IllegalStateException("invalid amount");
final long amount = getAmount();
// find spendable slot, and all non-slot outputs (spendableSubaddressIdx)
int usableSubaddressIdx = -1;
List<CoinsInfo> coins = wallet.getCoinsInfos(true);
Set<Integer> spendableSubaddressIdx = new HashSet<>();
PocketChangeSlot reserves = new PocketChangeSlot(); // everything not in a slot spendable
PocketChangeSlot[] slots = new PocketChangeSlot[POCKETCHANGE_SLOTS];
for (int i = 0; i < POCKETCHANGE_SLOTS; i++) {
slots[i] = new PocketChangeSlot();
}
for (CoinsInfo coin : coins) {
int subaddressIdx = coin.getAddressIndex();
if ((subaddressIdx < POCKETCHANGE_IDX) || (subaddressIdx > POCKETCHANGE_IDX_MAX)) { // spendableSubaddressIdx
reserves.add(coin);
spendableSubaddressIdx.add(subaddressIdx);
} else { // PocketChange slot
final int slotIdx = subaddressIdx - POCKETCHANGE_IDX;
slots[slotIdx].add(coin);
if (slots[slotIdx].getSpendableAmount() >= amount) {
usableSubaddressIdx = subaddressIdx;
}
}
}
long spendableAmount = reserves.getSpendableAmount();
final long pocketChangeAmount = setting.getAmount();
if (spendableAmount < pocketChangeAmount)
return; // do conventional transaction
Timber.d("usableSubaddressIdx=%d", usableSubaddressIdx);
if (usableSubaddressIdx >= 0) {
spendableSubaddressIdx.add(usableSubaddressIdx);
spendableAmount += slots[usableSubaddressIdx - POCKETCHANGE_IDX].getAmount();
} else {
// use everything
spendableSubaddressIdx.clear();
}
spendableAmount -= amount; // reserve the amount we need
// now we have the <usableSubaddressIdx> and all spendableSubaddressIdx subaddresses to use and how much spendableSubaddressIdx we have
// find any slots to fill if possible:
List<Integer> slotsToFill = new ArrayList<>();
List<Long> slotToFillAmounts = new ArrayList<>();
for (int i = 0; i < POCKETCHANGE_SLOTS; i++) {
if (slots[i].getAmount() < pocketChangeAmount) {
final long topupAmount = pocketChangeAmount - slots[i].getAmount();
if (topupAmount <= spendableAmount) {
slotsToFill.add(i);
slotToFillAmounts.add(topupAmount);
spendableAmount -= topupAmount;
Timber.d("FILL %d with %d", i, topupAmount);
}
}
}
String[] destinations;
long[] amounts;
while (true) {
destinations = new String[slotsToFill.size() + 1];
destinations[0] = getDestination();
amounts = new long[slotsToFill.size() + 1];
amounts[0] = getAmount();
if (slotsToFill.size() == 0) break;
for (int i = 0; i < slotsToFill.size(); i++) {
destinations[i + 1] = wallet.getSubaddress(slotsToFill.get(i) + POCKETCHANGE_IDX);
amounts[i + 1] = slotToFillAmounts.get(i);
}
final long fees = wallet.estimateTransactionFee(this) * 10; // pessimistic
if (fees < spendableAmount) break;
spendableAmount += slotToFillAmounts.get(0);
slotsToFill.remove(0);
slotToFillAmounts.remove(0);
}
this.destinations = destinations;
this.amounts = amounts;
subaddresses = new int[spendableSubaddressIdx.size()];
int i = 0;
for (int subaddressIdx : spendableSubaddressIdx) {
subaddresses[i++] = subaddressIdx;
}
} }
} }

View File

@ -41,10 +41,6 @@ public class TxDataBtc extends TxData {
super(); super();
} }
public TxDataBtc(TxDataBtc txDataBtc) {
super(txDataBtc);
}
@Override @Override
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, 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) { if (txData instanceof TxDataBtc) {
((TxDataBtc) txData).setBtcAddress(etAddress.getEditText().getText().toString()); ((TxDataBtc) txData).setBtcAddress(etAddress.getEditText().getText().toString());
((TxDataBtc) txData).setBtcSymbol(selectedCrypto.getSymbol()); ((TxDataBtc) txData).setBtcSymbol(selectedCrypto.getSymbol());
txData.setDestinationAddress(null); txData.setDestination(null);
ServiceHelper.ASSET = selectedCrypto.getSymbol().toLowerCase(); ServiceHelper.ASSET = selectedCrypto.getSymbol().toLowerCase();
} else { } else {
txData.setDestinationAddress(etAddress.getEditText().getText().toString()); txData.setDestination(etAddress.getEditText().getText().toString());
ServiceHelper.ASSET = null; ServiceHelper.ASSET = null;
} }
txData.setUserNotes(new UserNotes(etNotes.getEditText().getText().toString())); 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.ImageButton;
import android.widget.TextView; 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.R;
import com.m2049r.xmrwallet.data.BarcodeData; import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;

View File

@ -348,7 +348,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
showProgress(3, getString(R.string.label_send_progress_create_tx)); showProgress(3, getString(R.string.label_send_progress_create_tx));
final TxData txData = sendListener.getTxData(); final TxData txData = sendListener.getTxData();
txData.setDestinationAddress(xmrtoOrder.getXmrAddress()); txData.setDestination(xmrtoOrder.getXmrAddress());
txData.setAmount(xmrtoOrder.getXmrAmount()); txData.setAmount(xmrtoOrder.getXmrAmount());
getActivityCallback().onPrepareSend(xmrtoOrder.getOrderId(), txData); getActivityCallback().onPrepareSend(xmrtoOrder.getOrderId(), txData);
} }

View File

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

View File

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

View File

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

View File

@ -189,7 +189,6 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
} }
private void showLock() { private void showLock() {
Timber.d("UNLOCK %d:%d", infoItem.unlockTime, infoItem.blockheight);
if (infoItem.unlockTime == 0) { if (infoItem.unlockTime == 0) {
ivLock.setVisibility(View.GONE); ivLock.setVisibility(View.GONE);
return; return;
@ -220,7 +219,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ivTxType.setVisibility(View.GONE); 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) { if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount)); tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
} else { } 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; package com.m2049r.xmrwallet.model;
import lombok.Getter;
import lombok.Setter;
public class PendingTransaction { public class PendingTransaction {
static { static {
System.loadLibrary("monerujo"); System.loadLibrary("monerujo");
@ -63,8 +66,6 @@ public class PendingTransaction {
Priority(int value) { Priority(int value) {
this.value = value; this.value = value;
} }
} }
public Status getStatus() { public Status getStatus() {
@ -95,4 +96,11 @@ public class PendingTransaction {
public native long getTxCount(); 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; package com.m2049r.xmrwallet.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import timber.log.Timber; import timber.log.Timber;
@ -61,22 +63,14 @@ public class TransactionHistory {
private List<TransactionInfo> transactions = new ArrayList<>(); private List<TransactionInfo> transactions = new ArrayList<>();
void refreshWithNotes(Wallet wallet) { public void refreshWithNotes(Wallet wallet) {
refresh(); refresh();
loadNotes(wallet); loadNotes(wallet);
} }
private void refresh() { private void refresh() {
List<TransactionInfo> transactionInfos = refreshJ(); transactions = refreshJ(accountIndex);
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;
} }
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.unlockTime = unlockTime;
this.subaddressLabel = subaddressLabel; this.subaddressLabel = subaddressLabel;
this.transfers = transfers; 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() { public boolean isConfirmed() {
@ -138,6 +155,7 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(txKey); out.writeString(txKey);
out.writeString(notes); out.writeString(notes);
out.writeString(address); out.writeString(address);
out.writeLong(netAmount);
} }
public static final Parcelable.Creator<TransactionInfo> CREATOR = new Parcelable.Creator<TransactionInfo>() { 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(); txKey = in.readString();
notes = in.readString(); notes = in.readString();
address = in.readString(); address = in.readString();
netAmount = in.readLong();
} }
@Override @Override

View File

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

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

View File

@ -225,7 +225,7 @@ public class ExchangeEditText extends LinearLayout {
// make progress circle gray // make progress circle gray
pbExchange.getIndeterminateDrawable(). pbExchange.getIndeterminateDrawable().
setColorFilter(ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant), setColorFilter(ThemeHelper.getThemedColor(getContext(), com.google.android.material.R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY); android.graphics.PorterDuff.Mode.MULTIPLY);
sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

View File

@ -177,7 +177,7 @@ public class ExchangeView extends LinearLayout {
// make progress circle gray // make progress circle gray
pbExchange.getIndeterminateDrawable(). pbExchange.getIndeterminateDrawable().
setColorFilter(ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant), setColorFilter(ThemeHelper.getThemedColor(getContext(), com.google.android.material.R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY); android.graphics.PorterDuff.Mode.MULTIPLY);
sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

View File

@ -23,7 +23,7 @@ public class PasswordEntryView extends TextInputLayout implements TextWatcher {
} }
public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs) { public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs, R.attr.textInputStyle); super(context, attrs, com.google.android.material.R.attr.textInputStyle);
} }
public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorPrimaryVariant"
android:pathData="M15,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8 8,-3.58 8,-8 -3.58,-8 -8,-8zM15,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z" />
<path
android:fillColor="?attr/colorPrimaryVariant"
android:pathData="M3,12c0,-2.61 1.67,-4.83 4,-5.65V4.26C3.55,5.15 1,8.27 1,12s2.55,6.85 6,7.74v-2.09c-2.33,-0.82 -4,-3.04 -4,-5.65z" />
</vector>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_toll" />
<TextView
style="@style/MoneroText.Large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pocketchange_title"
android:textAlignment="textStart" />
<TextView
style="@style/MoneroText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top"
android:text="@string/pocketchange_info"
android:textAlignment="textStart" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="@dimen/header_top"
android:layout_marginEnd="16dp">
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/pocketchange_create_title" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchPocketChange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true" />
</RelativeLayout>
<LinearLayout
android:id="@+id/llAmount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="@dimen/header_top"
android:layout_marginEnd="16dp">
<TextView
style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/tx_amount" />
<TextView
android:id="@+id/seekbar_value"
style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:text="@string/pocketchange_amount" />
</RelativeLayout>
<com.google.android.material.slider.Slider
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:stepSize="1"
android:value="1"
android:valueFrom="0"
android:valueTo="5"
app:labelBehavior="gone"
app:tickVisible="true" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -38,7 +38,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical" /> android:orientation="vertical" />
<RelativeLayout <RelativeLayout

View File

@ -21,8 +21,8 @@
style="@style/MoneroText" style="@style/MoneroText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:textAlignment="textStart" android:textAlignment="textStart"
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" /> tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
@ -103,6 +103,31 @@
tools:text="143.008000000000" /> tools:text="143.008000000000" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/llPocketChange"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
style="@style/MoneroLabel.Gray"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/pocketchange_title"
android:textAlignment="textStart" />
<TextView
android:id="@+id/tvTxChange"
style="@style/MoneroText.Gray"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textAlignment="textEnd"
tools:text="143.008000000000" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -59,6 +59,15 @@
android:transitionName="@string/tx_details_transition_name" android:transitionName="@string/tx_details_transition_name"
tools:text="+ 1,092.00229" /> tools:text="+ 1,092.00229" />
<TextView
android:id="@+id/tvTxPocketChangeAmount"
style="@style/MoneroText.PosPocketChange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:selectAllOnFocus="true"
android:textIsSelectable="true"
tools:text="PocketChange 1,092.00229" />
<TextView <TextView
android:id="@+id/tvTxFee" android:id="@+id/tvTxFee"
style="@style/MoneroText.PosFee" style="@style/MoneroText.PosFee"

View File

@ -10,28 +10,16 @@
android:title="@string/menu_streetmode" android:title="@string/menu_streetmode"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/action_rename"
android:orderInCategory="200"
android:title="@string/menu_rename"
app:showAsAction="never" />
<item
android:id="@+id/action_subaddresses"
android:orderInCategory="250"
android:title="@string/subbaddress_title"
app:showAsAction="never" />
<item <item
android:id="@+id/action_help_wallet" android:id="@+id/action_help_wallet"
android:orderInCategory="300" android:orderInCategory="150"
android:title="@string/menu_help" android:title="@string/menu_help"
app:showAsAction="never" /> app:showAsAction="never" />
<item <item
android:id="@+id/action_credits" android:id="@+id/action_rename"
android:orderInCategory="400" android:orderInCategory="200"
android:title="@string/label_credits" android:title="@string/menu_rename"
app:showAsAction="never" /> app:showAsAction="never" />
<item <item
@ -45,4 +33,17 @@
android:orderInCategory="600" android:orderInCategory="600"
android:title="@string/menu_rescan" android:title="@string/menu_rescan"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/action_subaddresses"
android:orderInCategory="700"
android:title="@string/subbaddress_title"
app:showAsAction="never" />
<item
android:id="@+id/action_pocketchange"
android:orderInCategory="800"
android:title="@string/pocketchange_title"
app:showAsAction="never" />
</menu> </menu>

View File

@ -442,4 +442,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -443,4 +443,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -444,4 +444,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -442,4 +442,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -2,13 +2,13 @@
<resources> <resources>
<string name="wallet_activity_name">Monedero</string> <string name="wallet_activity_name">Monedero</string>
<string name="menu_about">Acerca De</string> <string name="menu_about">Legal</string>
<string name="menu_privacy">Política de Privacidad</string> <string name="menu_privacy">Política de Privacidad</string>
<string name="menu_share">Compartir</string> <string name="menu_share">Compartir</string>
<string name="menu_help">Ayuda</string> <string name="menu_help">Ayuda</string>
<string name="menu_receive">Recibir</string> <string name="menu_receive">Recibir</string>
<string name="menu_rename">Renombrar</string> <string name="menu_rename">Cambiar nombre</string>
<string name="menu_backup">Copia de seguridad</string> <string name="menu_backup">Copia de seguridad</string>
<string name="menu_changepw">Cambiar contraseña</string> <string name="menu_changepw">Cambiar contraseña</string>
@ -23,7 +23,7 @@
<string name="label_ok">Aceptar</string> <string name="label_ok">Aceptar</string>
<string name="label_cancel">Cancelar</string> <string name="label_cancel">Cancelar</string>
<string name="label_close">Cerrar</string> <string name="label_close">Cerrar</string>
<string name="label_wallet_advanced_details">Información más detallada</string> <string name="label_wallet_advanced_details">Detalle de claves</string>
<string name="label_send_success">¡Éxito!</string> <string name="label_send_success">¡Éxito!</string>
<string name="label_send_done">Hecho</string> <string name="label_send_done">Hecho</string>
@ -39,12 +39,12 @@
<string name="label_send_address">Dirección de Destino</string> <string name="label_send_address">Dirección de Destino</string>
<string name="label_send_notes">Notas</string> <string name="label_send_notes">Notas</string>
<string name="backup_progress">Copia de seguridad en progreso</string> <string name="backup_progress">Copia de seguridad en progreso&#8230;</string>
<string name="archive_progress">Archivado en progreso</string> <string name="archive_progress">Archivado en progreso&#8230;</string>
<string name="rename_progress">Cambio de nombre en progreso</string> <string name="rename_progress">Cambio de nombre en progreso&#8230;</string>
<string name="changepw_progress">Cambiando contraseña en progreso</string> <string name="changepw_progress">Cambiando contraseña en progreso&#8230;</string>
<string name="service_progress">Guardando todo\n¡Puede llevar un tiempo!</string> <string name="service_progress">Cerrando el monedero&#8230;\n¡Puede tardar un tiempo!</string>
<string name="backup_success">Copia de seguridad exitosa</string> <string name="backup_success">Copia de seguridad exitosa</string>
<string name="backup_failed">¡Copia de seguridad fallida!</string> <string name="backup_failed">¡Copia de seguridad fallida!</string>
@ -55,19 +55,19 @@
<string name="label_daemon">Nodo</string> <string name="label_daemon">Nodo</string>
<string name="status_wallet_loading">Cargando monedero&#8230;</string> <string name="status_wallet_loading">Cargando monedero&#8230;</string>
<string name="status_wallet_unloaded">Monedero guardado</string> <string name="status_wallet_unloaded">Monedero guardado</string>
<string name="status_wallet_unload_failed">¡Guardado de monedero fallido!</string> <string name="status_wallet_unload_failed">¡Fallo al guardar el monedero!</string>
<string name="status_wallet_connecting">Conectando&#8230;</string> <string name="status_wallet_connecting">Conectando&#8230;</string>
<string name="status_wallet_connect_failed">¡Conexión con el nodo fallida!\nComprueba el usuario/contraseña</string> <string name="status_wallet_connect_failed">¡Conexión con el nodo fallida!\nComprueba el usuario/contraseña</string>
<string name="status_wallet_node_invalid">¡Nodo inválido!\nInténtalo con otro.</string> <string name="status_wallet_node_invalid">¡Nodo inválido!\nInténtalo con otro.</string>
<string name="status_wallet_connect_ioex">¡No se puede alcanzar el nodo!\nInténtalo de nuevo o prueba otro.</string> <string name="status_wallet_connect_ioex">¡No se puede conectar con el nodo!\nInténtalo de nuevo o prueba otro.</string>
<string name="status_wallet_disconnected">Desconectado</string> <string name="status_wallet_disconnected">Desconectado</string>
<string name="status_transaction_failed">Transacción fallida: %1$s</string> <string name="status_transaction_failed">Transacción fallida: %1$s</string>
<string name="service_busy">Todavía estoy ocupado con tu último monedero &#8230;</string> <string name="service_busy">Todavía estoy ocupado con el último monedero &#8230;</string>
<string name="prompt_rename">Renombrar %1$s</string> <string name="prompt_rename">Nuevo nombre para %1$s</string>
<string name="prompt_changepw">Nueva contraseña para %1$s</string> <string name="prompt_changepw">Nueva contraseña para %1$s</string>
<string name="prompt_changepwB">Repetir contraseña para %1$s</string> <string name="prompt_changepwB">Repetir contraseña para %1$s</string>
@ -88,21 +88,21 @@
<string name="xmr_unconfirmed_amount">+ %1$s %2$s sin confirmar</string> <string name="xmr_unconfirmed_amount">+ %1$s %2$s sin confirmar</string>
<string name="service_description">Servicio de Monerujo</string> <string name="service_description">Monedero abierto</string>
<string name="status_synced">Sincronizado:</string> <string name="status_synced">Sincronizado:</string>
<string name="status_remaining">bloques restantes</string> <string name="status_remaining">bloques restantes</string>
<string name="status_syncing">Escaneando:</string> <string name="status_syncing">Escaneando:</string>
<string name="message_strorage_not_writable">¡No se puede escribir en el almacenamiento externo! ¡Pánico!</string> <string name="message_strorage_not_writable">¡Oh no! ¡No se puede escribir en el almacenamiento externo!</string>
<string name="message_strorage_not_permitted">¡De verdad necesitamos ese permiso para el almacenamiento externo!</string> <string name="message_strorage_not_permitted">¡De verdad necesitamos ese permiso para el almacenamiento externo!</string>
<string name="message_camera_not_permitted">Sin cámara = ¡Sin escaneo de QR!</string> <string name="message_camera_not_permitted">¡No se puede escanear un QR sin acceso a la cámara!</string>
<string name="label_copy_viewkey">Clave de Vista</string> <string name="label_copy_viewkey">Clave de Vista</string>
<string name="label_copy_address">Dirección Pública</string> <string name="label_copy_address">Dirección Pública</string>
<string name="message_copy_viewkey">¡Clave de vista copiada al portapapeles!</string> <string name="message_copy_viewkey">¡Clave de vista copiada al portapapeles!</string>
<string name="message_copy_address">¡Dirección del monedero copiada al portapapeles!</string> <string name="message_copy_address">¡Dirección del monedero copiada al portapapeles!</string>
<string name="message_nocopy">¡Copia desactivada por motivos de seguridad!</string> <string name="message_nocopy">¡Copia no permitida para tu seguridad!</string>
<string name="message_exchange_failed">¡No se ha podido obtener la tasa de cambio!\nUsa XMR/XMR o inténtalo de nuevo</string> <string name="message_exchange_failed">¡No se ha podido obtener la tasa de cambio!\nUsa XMR/XMR o inténtalo de nuevo</string>
@ -112,25 +112,24 @@
<string name="generate_fingerprint_hint">Permitir abrir usando huella dactilar</string> <string name="generate_fingerprint_hint">Permitir abrir usando huella dactilar</string>
<string name="generate_fingerprint_warn"><![CDATA[ <string name="generate_fingerprint_warn"><![CDATA[
<strong>Autenticación por huella</strong> <strong>Autenticación por huella</strong>
<p>Con la autenticación por huella dactilar activada, puedes acceder al balance y recibir fondos <p>Con la autenticación por huella dactilar activada, puedes abrir tu monedero y recibir fondos
sin la necesidad de ingresar tu contraseña.</p> sin la necesidad de ingresar tu contraseña.</p>
<p>Sin embargo, por seguridad extra, Monerujo va a requerir tu contraseña para ver la información <p>Sin embargo, para tu seguridad, Monerujo va a requerir tu contraseña para ver los secretos de tu monedero o enviar fondos.</p>
sensible de tu monedero o enviar fondos.</p>
<strong>Advertencia de seguridad</strong> <strong>Advertencia de seguridad</strong>
<p>Si bien es cómodo, recuerda que cualquier persona que tenga acceso a tu huella dactilar <p>Si bien es cómodo, recuerda que cualquier persona que tenga acceso a tu huella dactilar
va a ser capaz de mirar el balance de tu monedero.</p> va a ser capaz de mirar el balance de tu monedero.</p>
<p>Por ejemplo, un actor malicioso cercano podría abrir tu monedero con tu dedo mientras duermes.</p> <p>Por ejemplo, alguien podría abrir tu monedero con tu dedo mientras duermes.</p>
<strong>¿Estás seguro de activar esta función?</strong> <strong>¿Estás seguro de activar esta función?</strong>
]]></string> ]]></string>
<string name="generate_bad_passwordB">Contraseñas no coinciden</string> <string name="generate_bad_passwordB">Contraseñas no coinciden</string>
<string name="generate_empty_passwordB">Contraseña no puede estar vacía</string> <string name="generate_empty_passwordB">La contraseña no puede estar vacía</string>
<string name="generate_buttonGenerate">¡Házme ya un monedero!</string> <string name="generate_buttonGenerate">¡Házme ya un monedero!</string>
<string name="generate_button_accept">¡Ya anote todo!</string> <string name="generate_button_accept">¡Ya anote todo!</string>
<string name="generate_wallet_name">¡Dame un nombre!</string> <string name="generate_wallet_name">¡Dame un nombre!</string>
<string name="generate_wallet_exists">¡El monedero ya existe!</string> <string name="generate_wallet_exists">¡El monedero ya existe!</string>
<string name="generate_wallet_dot">No puede empezar con .</string> <string name="generate_wallet_dot">No puede empezar con .</string>
<string name="generate_wallet_creating">Creando monedero</string> <string name="generate_wallet_creating">Creando monedero&#8230;</string>
<string name="generate_wallet_created">Monedero creada</string> <string name="generate_wallet_created">Monedero creada</string>
<string name="generate_restoreheight_error">Introduce un número o una fecha (AAAA-MM-DD)</string> <string name="generate_restoreheight_error">Introduce un número o una fecha (AAAA-MM-DD)</string>
@ -140,17 +139,17 @@
<string name="generate_wallet_type_seed">Semilla</string> <string name="generate_wallet_type_seed">Semilla</string>
<string name="generate_wallet_type_view">Ver</string> <string name="generate_wallet_type_view">Ver</string>
<string name="generate_address_hint">Dirección Pública</string> <string name="generate_address_hint">Dirección pública</string>
<string name="generate_viewkey_hint">Clave de Vista</string> <string name="generate_viewkey_hint">Clave de vista</string>
<string name="generate_spendkey_hint">Clave de Gasto</string> <string name="generate_spendkey_hint">Clave de gasto</string>
<string name="generate_mnemonic_hint">Semilla Mnemotécnica de 25 Palabras</string> <string name="generate_mnemonic_hint">Semilla mnemotécnica</string>
<string name="generate_restoreheight_hint">Altura o Fecha (YYYY-MM-DD) de Restauración</string> <string name="generate_restoreheight_hint">Altura o fecha (YYYY-MM-DD) de restauración</string>
<string name="generate_address_label">Dirección Pública</string> <string name="generate_address_label">Dirección pública</string>
<string name="generate_viewkey_label">Clave de Vista</string> <string name="generate_viewkey_label">Clave de vista</string>
<string name="generate_spendkey_label">Clave de Gasto</string> <string name="generate_spendkey_label">Clave de gasto</string>
<string name="generate_mnemonic_label">Semilla Mnemotécnica</string> <string name="generate_mnemonic_label">Semilla mnemotécnica</string>
<string name="generate_crazypass_label">Contraseña de restauración para el archivo del monedero</string> <string name="generate_crazypass_label">Clave loca para restaurar el monedero</string>
<string name="generate_check_key">Introduce una clave válida</string> <string name="generate_check_key">Introduce una clave válida</string>
<string name="generate_check_address">Introduce una dirección válida</string> <string name="generate_check_address">Introduce una dirección válida</string>
@ -181,19 +180,19 @@
<string name="tx_timestamp">Marca de tiempo</string> <string name="tx_timestamp">Marca de tiempo</string>
<string name="tx_id">ID de Transacción</string> <string name="tx_id">ID de Transacción</string>
<string name="tx_key">Clave de Transacción</string> <string name="tx_key">Clave de transacción</string>
<string name="tx_destination">Destino</string> <string name="tx_destination">Destino</string>
<string name="tx_paymentId">ID de Pago</string> <string name="tx_paymentId">ID de pago</string>
<string name="tx_blockheight">Bloque</string> <string name="tx_blockheight">Bloque</string>
<string name="tx_amount">Monto</string> <string name="tx_amount">Monto</string>
<string name="tx_fee">Comisión</string> <string name="tx_fee">Comisión</string>
<string name="tx_transfers">Transferencias</string> <string name="tx_transfers">Transferencias</string>
<string name="tx_notes">Notas</string> <string name="tx_notes">Notas</string>
<string name="tx_notes_hint">(opcional)</string> <string name="tx_notes_hint">(opcional)</string>
<string name="tx_title">Detalles de la Transacción</string> <string name="tx_title">Detalles de la transacción</string>
<string name="tx_pending">PENDIENTE</string> <string name="tx_pending">PENDIENTE</string>
<string name="tx_failed">FALLIDO</string> <string name="tx_failed">FALLIDA</string>
<string name="receive_amount_hint">Monto</string> <string name="receive_amount_hint">Monto</string>
<string name="receive_cannot_open">¡No se ha podido abrir el monedero!</string> <string name="receive_cannot_open">¡No se ha podido abrir el monedero!</string>
@ -202,19 +201,19 @@
<string name="receive_amount_negative">Min. 0</string> <string name="receive_amount_negative">Min. 0</string>
<string name="receive_amount_nan">XMR no es un número</string> <string name="receive_amount_nan">XMR no es un número</string>
<string name="details_alert_message">Se va a mostrar información delicada.\n¡Mira por encima del hombro!</string> <string name="details_alert_message">Se va a mostrar información delicada. Cualquiera con acceso a ella tendría control sobre tus fondos.\n¡Mira detrás tuyo!</string>
<string name="details_alert_yes">Estoy seguro</string> <string name="details_alert_yes">Estoy seguro</string>
<string name="details_alert_no">¡Llévame de vuelta!</string> <string name="details_alert_no">No muestres nada</string>
<string name="details_title">Detalles</string> <string name="details_title">Secretos del monedero</string>
<string name="delete_alert_message">Este monedero será borrado. Tus fondos se irán para siempre a menos que tengas tu semilla o una copia de seguridad funcional para recuperarlo.</string> <string name="delete_alert_message">Este monedero será borrado.\nTus fondos se perderán para siempre sino tienes tu semilla mnemotéctica o una copia de seguridad y su correspondiente clave loca para recuperarla.\nPuedes encontrar ambas entre los secretos de tu monedero.</string>
<string name="delete_alert_yes">¡Sí, hazlo!</string> <string name="delete_alert_yes">Sí, hazlo</string>
<string name="delete_alert_no">¡No, gracias!</string> <string name="delete_alert_no">No lo hagas</string>
<string name="fab_create_new">Crear nuevo monedero</string> <string name="fab_create_new">Crear nuevo monedero</string>
<string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string> <string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string>
<string name="fab_restore_key">Restaurar monedero con claves privadas</string> <string name="fab_restore_key">Restaurar monedero con claves</string>
<string name="fab_restore_seed">Restaurar monedero con semilla de 25 palabras</string> <string name="fab_restore_seed">Restaurar monedero con semilla</string>
<string name="info_xmrto"><![CDATA[ <string name="info_xmrto"><![CDATA[
<b>Ingresaste una dirección %1$s</b><br/> <b>Ingresaste una dirección %1$s</b><br/>
<i>Vas a enviar XMR y el destinatario recibirá %1$s usando el servicio <b>SideShift.ai</b>.</i> <i>Vas a enviar XMR y el destinatario recibirá %1$s usando el servicio <b>SideShift.ai</b>.</i>
@ -223,22 +222,22 @@
<string name="info_send_xmrto_paid">Confirmación pendiente</string> <string name="info_send_xmrto_paid">Confirmación pendiente</string>
<string name="info_send_xmrto_unpaid">Pago pendiente</string> <string name="info_send_xmrto_unpaid">Pago pendiente</string>
<string name="info_send_xmrto_error">Error de SideShift.ai (%1$s)</string> <string name="info_send_xmrto_error">Error de SideShift.ai (%1$s)</string>
<string name="info_send_xmrto_sent">%1$s Enviados!</string> <string name="info_send_xmrto_sent">%1$s enviados!</string>
<string name="info_send_xmrto_query">Consultando &#8230;</string> <string name="info_send_xmrto_query">Consultando &#8230;</string>
<string name="info_send_xmrto_parms"><![CDATA[ <string name="info_send_xmrto_parms"><![CDATA[
<b>Puedes enviar %1$s &#8212; %2$s %4$s</b>.<br/> <b>Puedes enviar %1$s &#8212; %2$s %4$s</b>.<br/>
<i><b>SideShift.ai</b> está ofreciendo una tasa de cambio de <b>%3$s %4$s/XMR</b> <u>en este momento</u></i>. <i><b>SideShift.ai</b> está ofreciendo una tasa de cambio de <b>%3$s %4$s/XMR</b> <u>en este momento</u></i>.
]]></string> ]]></string>
<string name="send_available_btc">Saldo: %2$s %3$s (%1$s XMR)</string> <string name="send_available_btc">Saldo: %2$s %3$s (%1$s XMR)</string>
<string name="label_send_progress_xmrto_create">Creando orden SideShift.ai</string> <string name="label_send_progress_xmrto_create">Creando orden en SideShift.ai</string>
<string name="label_send_progress_xmrto_query">Consultando orden SideShift.ai</string> <string name="label_send_progress_xmrto_query">Consultando orden en SideShift.ai</string>
<string name="label_send_progress_create_tx">Preparando transacción Monero</string> <string name="label_send_progress_create_tx">Preparando transacción de Monero</string>
<string name="label_send_progress_queryparms">Consultando parámetros SideShift.ai</string> <string name="label_send_progress_queryparms">Consultando parámetros en SideShift.ai</string>
<string name="label_generic_xmrto_error">ERROR SideShift.ai</string> <string name="label_generic_xmrto_error">Error de SideShift.ai</string>
<string name="text_generic_xmrto_error">Código: %1$d</string> <string name="text_generic_xmrto_error">Código: %1$d</string>
<string name="text_retry">Toca para reintentar</string> <string name="text_retry">Toca para reintentar</string>
<string name="text_noretry_monero">Parece que estamos atascados!</string> <string name="text_noretry_monero">¡Parece que está atascada!</string>
<string name="text_noretry">Oh-oh, parece que SideShift.ai no está disponible ahora!</string> <string name="text_noretry">Oh no, parece que SideShift.ai no está disponible ahora.</string>
<string name="text_send_btc_amount">%1$s %3$s = %2$s XMR</string> <string name="text_send_btc_amount">%1$s %3$s = %2$s XMR</string>
<string name="text_send_btc_rate">(Cambio: %1$s %2$s/XMR)</string> <string name="text_send_btc_rate">(Cambio: %1$s %2$s/XMR)</string>
<string name="label_send_btc_xmrto_info">Visita https://sideshift.ai para soporte y rastreo</string> <string name="label_send_btc_xmrto_info">Visita https://sideshift.ai para soporte y rastreo</string>
@ -246,42 +245,42 @@
<string name="label_send_btc_xmrto_key">Clave secreta SideShift.ai</string> <string name="label_send_btc_xmrto_key">Clave secreta SideShift.ai</string>
<string name="label_send_btc_address">Dirección %1$s destino</string> <string name="label_send_btc_address">Dirección %1$s destino</string>
<string name="label_send_btc_amount">Monto</string> <string name="label_send_btc_amount">Monto</string>
<string name="send_xmrto_timeout">Oye, tardaste demasiado!</string> <string name="send_xmrto_timeout">¡Oye, tardaste demasiado!</string>
<string name="label_copy_xmrtokey">Clave</string> <string name="label_copy_xmrtokey">Clave</string>
<string name="message_copy_xmrtokey">¡Clave copiada al portapapeles!</string> <string name="message_copy_xmrtokey">¡Clave copiada al portapapeles!</string>
<string name="send_send_label">Enviar mis preciados moneroj</string> <string name="send_send_label">Enviar mis preciados moneroj</string>
<string name="send_send_timed_label">Gastar mis preciados moneroj (%1$s)</string> <string name="send_send_timed_label">Enviar mis preciados moneroj (%1$s)</string>
<string name="send_address_invalid">No es una dirección válida</string> <string name="send_address_invalid">No es una dirección válida</string>
<string name="send_fee_btc_label">Comisión (XMR)</string> <string name="send_fee_btc_label">Comisión (XMR)</string>
<string name="send_total_btc_label">Total (XMR)</string> <string name="send_total_btc_label">Total (XMR)</string>
<string name="send_amount">%1$s XMR</string> <string name="send_amount">%1$s XMR</string>
<string name="send_fee">+%1$s Comisión</string> <string name="send_fee">+%1$s Comisión</string>
<string name="about_whoami">Soy Monerujo</string> <string name="about_whoami">Monerujo</string>
<string name="info_send_xmrto_success_order_label">Orden SideShift.ai</string> <string name="info_send_xmrto_success_order_label">Orden de SideShift.ai</string>
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string> <string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
<string name="info_ledger_enabled">Ledger activado, toca para más info.</string> <string name="info_ledger_enabled">Ledger activado, toca para más info.</string>
<string name="accounts_drawer_new">Crear Cuenta</string> <string name="accounts_drawer_new">Crear cuenta</string>
<string name="accounts_new">Nueva cuenta agregada #%1$d</string> <string name="accounts_new">Nueva cuenta agregada #%1$d</string>
<string name="tx_account"># de cuenta</string> <string name="tx_account"># de cuenta</string>
<string name="send_sweepall">¡Enviar todos los fondos confirmados en esta cuenta!</string> <string name="send_sweepall">¿Quieres enviar todos los fondos de esta cuenta?</string>
<string name="tx_subaddress">Subdirección</string> <string name="tx_subaddress">Subdirección</string>
<string name="generate_address_label_sub">Subdirecciones Públicas #%1$d: %2$s</string> <string name="generate_address_label_sub">Subdirecciones públicas #%1$d: %2$s</string>
<string name="menu_language">Lenguaje</string> <string name="menu_language">Idioma</string>
<string name="language_system_default">Usar Idioma del Sistema</string> <string name="language_system_default">Usar el del sistema</string>
<string name="fab_restore_ledger">Restaurar desde Ledger Nano</string> <string name="fab_restore_ledger">Restaurar desde Ledger Nano</string>
<string name="progress_ledger_progress">Comunicándose con Ledger</string> <string name="progress_ledger_progress">Comunicándose con Ledger</string>
<string name="progress_ledger_confirm">¡Confirmación en Ledger requerida!</string> <string name="progress_ledger_confirm">Esperando confirmación en Ledger</string>
<string name="progress_ledger_lookahead">Recuperando subdirecciones</string> <string name="progress_ledger_lookahead">Recuperando subdirecciones</string>
<string name="progress_ledger_verify">Verificando claves</string> <string name="progress_ledger_verify">Verificando claves</string>
<string name="progress_ledger_opentx">Realizando cálculos alocados</string> <string name="progress_ledger_opentx">Realizando cálculos locos&#8230;</string>
<string name="progress_ledger_mlsag">Cosas de hash</string> <string name="progress_ledger_mlsag">Hasheando&#8230;</string>
<string name="open_wallet_ledger_missing">Por favor (re)conecta el dispositivo Ledger</string> <string name="open_wallet_ledger_missing">Por favor reconecta el dispositivo Ledger</string>
<string name="accounts_progress_new">Creando cuenta</string> <string name="accounts_progress_new">Creando cuenta</string>
@ -291,45 +290,45 @@
<string name="receive_desc_hint">Descripción (opcional)</string> <string name="receive_desc_hint">Descripción (opcional)</string>
<string name="send_address_not_openalias">Dirección OpenAlias no disponible</string> <string name="send_address_not_openalias">Dirección OpenAlias no disponible</string>
<string name="send_address_openalias">OpenAlias asegurado &#x2714;</string> <string name="send_address_openalias">OpenAlias seguro &#x2714;</string>
<string name="send_address_resolve_openalias">Resolviendo OpenAlias&#8230;</string> <string name="send_address_resolve_openalias">Resolviendo OpenAlias&#8230;</string>
<string name="send_address_no_dnssec">OpenAlias sin DNSSEC - la drección puede ser falsificada</string> <string name="send_address_no_dnssec">OpenAlias sin DNSSEC - la dirección podría ser falsificada</string>
<string name="status_wallet_connect_wrongversion">Versión de nodo incompatible - ¡por favor actualiza!</string> <string name="status_wallet_connect_wrongversion">Versión de nodo incompatible - ¡por favor actualiza!</string>
<string name="menu_info">Detalles</string><!--Changed to: Show Secrets!--> <string name="menu_info">Ver secretos</string><!--Changed to: Show Secrets!-->
<string name="menu_streetmode">Modo Público</string> <string name="menu_streetmode">Modo calle</string>
<string name="info_nodes_enabled">Nodo-o-matiC habilitado, toque para más información.</string> <string name="info_nodes_enabled">Nodo-matiC habilitado, toque para más información.</string>
<string name="node_height">Último bloque actualizado: %1$s</string> <string name="node_height">Último bloque actualizado: %1$s</string>
<string name="label_nodes">Nodos</string> <string name="label_nodes">Nodos</string>
<string name="node_name_hint">Nombre del Nodo (Opcional)</string> <string name="node_name_hint">Nombre (Opcional)</string>
<string name="node_address_hint">Nombre del Host</string> <string name="node_address_hint">Dirección del nodo</string>
<string name="node_port_hint">Puerto</string> <string name="node_port_hint">Puerto</string>
<string name="node_user_hint">Usuario (Opcional)</string> <string name="node_user_hint">Usuario (Opcional)</string>
<string name="node_pass_hint">Contraseña (Opcional)</string> <string name="node_pass_hint">Contraseña (Opcional)</string>
<string name="node_host_unresolved">No se puede resolver el host</string> <string name="node_host_unresolved">No se puede encontrar al nodo</string>
<string name="node_host_empty">¡Necesitamos esto!</string> <string name="node_host_empty">¡Necesitas esto!</string>
<string name="node_port_numeric">Debe ser numérico</string> <string name="node_port_numeric">Debe ser un número</string>
<string name="node_port_range">Debe ser 1&#8211;65535</string> <string name="node_port_range">Debe ser 1&#8211;65535</string>
<string name="node_fab_add">Agregar Nodo</string> <string name="node_fab_add">Agregar Nodo</string>
<string name="node_refresh_hint">¡Toca para refrescar!</string> <string name="node_refresh_hint">¡Toca para refrescar!</string>
<string name="node_test_error">ERROR DE CONECCIÓN %1$d</string> <string name="node_test_error">ERROR AL CONECTAR %1$d</string>
<string name="node_general_error">ERROR DE CONECCIÓN</string> <string name="node_general_error">No se pudo conectar</string>
<string name="node_auth_error">AUTENTIFICACIÓN FALLIDA</string> <string name="node_auth_error">AUTENTIFICACIÓN FALLIDA</string>
<string name="node_result_label">Resultados de la prueba:</string> <string name="node_result_label">Resultados:</string>
<string name="node_result">Altura: %1$s (v%2$d), Ping: %3$.0fms, IP: %4$s</string> <string name="node_result">Altura: %1$s (v%2$d)\nPing: %3$.0fms\nIP: %4$s</string>
<string name="node_testing">Probando el IP: %1$s &#8230;</string> <string name="node_testing">Probando al IP: %1$s&#8230;</string>
<string name="node_refresh_wait">Por favor espera a que termine el escaneo</string> <string name="node_refresh_wait">Por favor espera a que termine el escaneo</string>
<string name="node_create_hint">Toca para seleccionar o agregar nodos</string> <string name="node_create_hint">Toca para elegir un nodo</string>
<string name="node_pull_hint">Agrega nodos manualmente o tira para buscar</string> <string name="node_pull_hint">Tira para refrescar</string>
<string name="node_scanning">Escaneando la red&#8230;</string> <string name="node_scanning">Buscando nodos&#8230;</string>
<string name="node_nobookmark">Mejores %1$d nodos marcados automáticamente</string> <string name="node_nobookmark">Mejores %1$d nodos marcados automáticamente</string>
<string name="label_test">Probar</string><!--note: as in "Test a network connection"--> <string name="label_test">Probar</string><!--note: as in "Test a network connection"-->
<!-- please verify this means "Receiver" or "Recipient" as in "Receiver in the transaction" --> <!-- please verify this means "Receiver" or "Recipient" as in "Receiver in the transaction" -->
<string name="send_address_hint">Receptor</string> <string name="send_address_hint">Dirección del receptor</string>
<string name="street_sweep_amount">TODO!</string> <!-- as in: "everything in the account" = "all the money" --> <string name="street_sweep_amount">TODO!</string> <!-- as in: "everything in the account" = "all the money" -->
@ -339,26 +338,26 @@
<string name="bad_ledger_seed">¡Semilla de Ledger inválida!</string> <string name="bad_ledger_seed">¡Semilla de Ledger inválida!</string>
<string name="prompt_ledger_seed_warn">¡Ingresar tu semilla de Ledger aquí implica un riesgo importante!</string> <string name="prompt_ledger_seed_warn">¡Ingresar tu semilla de Ledger aquí implica un riesgo importante!</string>
<string name="label_restoreheight">Altura de Restauración</string> <!-- Restore Height --> <string name="label_restoreheight">Altura de restauración</string> <!-- Restore Height -->
<string name="toast_ledger_start_app">Arrancar aplicación de Monero en %1$s</string> <string name="toast_ledger_start_app">Arrancar aplicación de Monero en %1$s</string>
<string name="menu_rescan">¡Re-escanear!</string> <string name="menu_rescan">Re-escanear</string>
<string name="onboarding_agree">¡Entendido!</string> <string name="onboarding_agree">¡Entendido!</string>
<string name="onboarding_button_next">Siguiente</string> <string name="onboarding_button_next">Siguiente</string>
<string name="onboarding_button_ready">¡Estoy listo!</string> <string name="onboarding_button_ready">¡Estoy listo!</string>
<string name="onboarding_welcome_title">¡Bienvenido a Monerujo!</string> <string name="onboarding_welcome_title">¡Bienvenido a Monerujo!</string>
<string name="onboarding_welcome_information">Esta aplicación te permite crear y usar monederos de Monero. Puedes guardar tus dulces moneroj en ellos.</string> <string name="onboarding_welcome_information">Esta aplicación te permite crear y usar monederos. Puedes recibir y enviar dulces moneros con ella.</string>
<string name="onboarding_seed_title">Mantén segura tu semilla</string> <string name="onboarding_seed_title">Escribe tu semilla mnemotécnica</string>
<string name="onboarding_seed_information">La semilla otorga acceso total a quien la posee. Si la pierdes, nadie puede ayudarte a recuperarla y perderás tus preciados moneroj.</string> <string name="onboarding_seed_information">Esas palabras otorgan acceso total a tus fondos a quien las tengan. Si la pierdes, nadie puede ayudarte y perderás tus preciados moneros.</string>
<string name="onboarding_xmrto_title">Enviar Criptos</string> <string name="onboarding_xmrto_title">Cambia Monero</string>
<string name="onboarding_xmrto_information">Monerujo tiene SideShift.ai incorporado. Simplemente pega o escanea una dirección de BTC, LTC, ETH, DASH o DOGE y podrás enviar esas monedas usando tu XMR.</string> <string name="onboarding_xmrto_information">Monerujo tiene SideShift.ai incorporado. Simplemente ingresa una dirección de BTC, LTC, ETH, DASH o DOGE y podrás enviar esas monedas usando XMR.</string>
<string name="onboarding_nodes_title">Nodos, a tu manera</string> <string name="onboarding_nodes_title">Nodos, a tu manera</string>
<string name="onboarding_nodes_information">Los nodos son tu conexión con la red de Monero. Elige entre usar nodos públicos, o ir totalmente ciberpunk conectándote al tuyo propio.</string> <string name="onboarding_nodes_information">Los nodos son tu conexión con la red de Monero. Elige entre nodos públicos, o totalmente ciberpunk conectándote al tuyo propio.</string>
<string name="onboarding_fpsend_title">Enviar con tu huella</string> <string name="onboarding_fpsend_title">Envia con un dedo</string>
<string name="onboarding_fpsend_information">Ahora puedes enviar XMR con sólo tu huella dactilar si lo deseas. Para forzar el pedido de contraseña, simplemente desactiva la opción de huella.</string> <string name="onboarding_fpsend_information">Ahora puedes enviar XMR con sólo tu huella dactilar si lo deseas. Para extra seguridad, simplemente desactiva la opción y usa tu contraseña.</string>
<string name="menu_daynight">Tema</string> <string name="menu_daynight">Tema</string>
<string-array name="daynight_themes"> <string-array name="daynight_themes">
@ -368,7 +367,7 @@
</string-array> </string-array>
<string name="gunther_says">No hay nada aquí.\nPor favor crea o restaura un monedero.</string> <string name="gunther_says">No hay nada aquí.\nPor favor crea o restaura un monedero.</string>
<string name="menu_default_nodes">Restaurar los nodos por defecto</string> <string name="menu_default_nodes">Agregar nodos públicos</string>
<string name="toast_default_nodes">Restauración ya en proceso…</string> <string name="toast_default_nodes">Restauración ya en proceso…</string>
<string name="node_updated_now">Último bloque hace %1$d segundos</string> <string name="node_updated_now">Último bloque hace %1$d segundos</string>
@ -393,46 +392,51 @@
<b>Por favor ingresa o escanea una dirección de Monero.</b> <b>Por favor ingresa o escanea una dirección de Monero.</b>
]]></string> ]]></string>
<string name="subbaddress_title">Subdirecciones</string> <string name="subbaddress_title">Subdirecciones de la cuenta</string>
<string name="subbaddress_name_hint">Nombre de la subdirección</string> <string name="subbaddress_name_hint">Nombre de la subdirección</string>
<string name="max_subaddress_warning">Demasiadas direcciones sin usar. ¡Usa alguna antes de crear más!</string> <string name="max_subaddress_warning">Demasiadas direcciones sin usar. ¡Usa alguna antes de crear más!</string>
<string name="max_account_warning">Demasiadas cuentas sin usar. ¡Usa alguna antes de crear más!</string> <string name="max_account_warning">Demasiadas cuentas sin usar. ¡Usa alguna antes de crear más!</string>
<string name="subaddress_tx_label">Transacciones recibidas en esta subdirección:</string> <string name="subaddress_tx_label">Transacciones recibidas en esta subdirección:</string>
<string name="subaddress_notx_label">Aún no hay transacciones recibidas en esta subdirección.</string> <string name="subaddress_notx_label">Aún no hay transacciones recibidas en esta subdirección.</string>
<string name="subaddress_select_label">Elige una subdirección</string> <string name="subaddress_select_label">Elige una subdirección</string>
<string name="subaddress_details_hint">Presiona largo para ver detalles</string> <string name="subaddress_details_hint">Mantén presionado para ver los detalles</string>
<string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> <string name="menu_delete">Eliminar</string><!-- like: "Delete wallet!" -->
<string name="delete_failed">Delete failed!</string> <string name="delete_failed">¡Falló la eliminación!</string>
<string name="menu_restore">Import wallet</string> <string name="menu_restore">Importar monedero</string>
<string name="restore_failed">Import failed!</string> <string name="restore_failed">¡Falló la importación!</string>
<string name="menu_deletecache">Reset wallet!</string> <string name="menu_deletecache">Restaurar</string>
<string name="deletecache_alert_message">This wallet will be reset, losing all off-chain data (like notes, account &amp; subaddress names, private transaction keys, &#8230;)! Use this ONLY if this wallet is corrupt and does not load!</string> <string name="deletecache_alert_message">Este monedero será restaurado, y perderá toda la información personalizada (notas, nombres de cuentas y subdirecciones, claves particulares de transacciones&#8230;) Tus fondos no se verán comprometidos, pero usa esto SÓLO si el monedero está corrupto y no se puede abrir.</string>
<string name="node_tor_error">Tor required</string> <string name="node_tor_error">Requiere Tor</string>
<string name="node_waiting">\u00A0WAITING FOR NODE\u00A0</string> <string name="node_waiting">\u00A0ESPERANDO AL NODO\u00A0</string>
<string name="tor_enable_background">"Allow Background Starts" in Orbot Settings to use Tor!</string> <string name="tor_enable_background">Selecciona "Permitir inicios en segundo plano" en los ajustes de Orbot para usar Tor</string>
<string name="tor_noshift">SideShift.ai doesn\'t support Tor.\nDisable Tor to swap XMR.</string> <string name="tor_noshift">SideShift.ai no soporta Tor.\nDesactiva Tor para cambiar XMR.</string>
<string name="label_seed_offset_encrypt">Seed encryption (EXPERIMENTAL)</string> <string name="label_seed_offset_encrypt">Semilla encriptada (EXPERIMENTAL)</string>
<string name="seed_offset_hint">Seed Offset Phrase (optional)</string> <string name="seed_offset_hint">Palabra clave adicional</string>
<string name="menu_settings">Settings</string> <string name="menu_settings">Ajustes</string>
<string name="title_iface">Interface</string> <!-- like: User Intreface --> <string name="title_iface">Interfaz</string> <!-- like: User Interface -->
<string name="title_info">Information</string> <string name="title_info">Información</string>
<string name="setting_daynight">Day / Night</string> <string name="setting_daynight">Modo</string>
<string name="setting_theme">Style</string> <string name="setting_theme">Tema</string>
<string-array name="themes"> <string-array name="themes">
<item>Classic</item> <item>Classic</item>
<item>Baldaŭ</item> <!-- do not translate this one --> <item>Baldaŭ</item> <!-- do not translate this one -->
</string-array> </string-array>
<string name="message_qr_failed">Failed to create QR for sharing!</string> <string name="message_qr_failed">¡Error al crear QR!</string>
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Monto trabado hasta el bloque %1$d (faltan %2$d bloques ≈ %3$,.2f días)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Modo calle activado\nSólo se ºmostrarán transacciones nuevas</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -442,4 +442,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -682,4 +682,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -448,4 +448,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -446,4 +446,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -447,4 +447,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -447,4 +447,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -444,4 +444,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -444,4 +444,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -437,4 +437,9 @@ aqui.</string>
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -448,4 +448,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_amount">10 × %1$3.1f XMR</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -444,4 +444,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -448,4 +448,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -445,4 +445,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -443,4 +443,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -436,4 +436,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -442,4 +442,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -448,4 +448,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -369,4 +369,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -443,4 +443,9 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -22,4 +22,5 @@
<attr name="streetColorB" format="reference" /> <attr name="streetColorB" format="reference" />
<attr name="noticeColor" format="reference" /> <attr name="noticeColor" format="reference" />
<attr name="toolbarTextColor" format="reference" /> <attr name="toolbarTextColor" format="reference" />
</resources> </resources>

View File

@ -520,4 +520,13 @@
<string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string> <string name="tx_locked">Transaction amount locked until block %1$d (in %2$d blocks ≈ %3$,.2f days)</string>
<string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string> <string name="label_streetmode">Street Mode enabled\nOnly new transactions will be shown</string>
<string name="pocketchange_title" translatable="false">PocketChange</string>
<string name="pocketchange_amount" translatable="false">10 × %1$3.1f XMR</string>
<string name="pocketchange_tx_detail" translatable="false">PocketChange +%1$s</string>
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain 10 coins of the selected amount.</string>
<string name="pocketchange_create_title">Create Change</string>
<string name="label_apply">APPLY</string>
</resources> </resources>

View File

@ -158,6 +158,12 @@
<item name="android:textStyle">normal</item> <item name="android:textStyle">normal</item>
</style> </style>
<style name="MoneroText.PosPocketChange">
<item name="android:textSize">10sp</item>
<item name="android:textColor">?attr/positiveColor</item>
<item name="android:textStyle">normal</item>
</style>
<style name="MoneroText.PosNote"> <style name="MoneroText.PosNote">
<item name="android:textSize">12sp</item> <item name="android:textSize">12sp</item>
<!--item name="android:textColor">#FF4A4A4A</item--> <!--item name="android:textColor">#FF4A4A4A</item-->

View File

@ -1 +1 @@
MONERUJO_monero feature_v0.18.2.2 with monero release-v0.18.2.2-monerujo MONERUJO_monero feature_coins with monero release-v0.18.2.2-monerujo

View File

@ -260,6 +260,47 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0; virtual int lookupPaymentID(const std::string &payment_id) const = 0;
}; };
/**
* @brief The CoinsInfo - interface for displaying coins information
*/
struct CoinsInfo
{
virtual ~CoinsInfo() = 0;
virtual uint64_t blockHeight() const = 0;
virtual std::string hash() const = 0;
virtual size_t internalOutputIndex() const = 0;
virtual uint64_t globalOutputIndex() const = 0;
virtual bool spent() const = 0;
virtual bool frozen() const = 0;
virtual uint64_t spentHeight() const = 0;
virtual uint64_t amount() const = 0;
virtual bool rct() const = 0;
virtual bool keyImageKnown() const = 0;
virtual size_t pkIndex() const = 0;
virtual uint32_t subaddrIndex() const = 0;
virtual uint32_t subaddrAccount() const = 0;
virtual std::string address() const = 0;
virtual std::string addressLabel() const = 0;
virtual std::string keyImage() const = 0;
virtual uint64_t unlockTime() const = 0;
virtual bool unlocked() const = 0;
virtual std::string pubKey() const = 0;
virtual bool coinbase() const = 0;
};
struct Coins
{
virtual ~Coins() = 0;
virtual int count() const = 0;
virtual CoinsInfo * coin(int index) const = 0;
virtual std::vector<CoinsInfo*> getAll() const = 0;
virtual void refresh() = 0;
virtual void setFrozen(int index) = 0;
virtual void thaw(int index) = 0;
virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0;
};
struct SubaddressRow { struct SubaddressRow {
public: public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
@ -937,6 +978,7 @@ struct Wallet
virtual TransactionHistory * history() = 0; virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0; virtual AddressBook * addressBook() = 0;
virtual Coins * coins() = 0;
virtual Subaddress * subaddress() = 0; virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0; virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0; virtual void setListener(WalletListener *) = 0;

View File

@ -19,5 +19,5 @@ org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false android.nonTransitiveRClass=true
android.nonFinalResIds=false android.nonFinalResIds=false

View File

@ -1,5 +1,6 @@
#Mon May 22 20:01:18 CEST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME