subaddresses (#322)

This commit is contained in:
m2049r 2018-06-18 09:43:32 +02:00 committed by GitHub
parent 091538752b
commit 01e7693425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 268 additions and 60 deletions

View File

@ -552,9 +552,11 @@ Java_com_m2049r_xmrwallet_model_Wallet_setPassword(JNIEnv *env, jobject instance
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getAddressJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_Wallet_getAddressJ(JNIEnv *env, jobject instance,
jint accountIndex) { jint accountIndex,
jint addressIndex) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return env->NewStringUTF(wallet->address((uint32_t) accountIndex).c_str()); return env->NewStringUTF(
wallet->address((uint32_t) accountIndex, (uint32_t) addressIndex).c_str());
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -1027,13 +1029,44 @@ Java_com_m2049r_xmrwallet_model_Wallet_setSubaddressLabel(JNIEnv *env, jobject i
// virtual size_t numSubaddressAccounts() const = 0; // virtual size_t numSubaddressAccounts() const = 0;
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_numSubaddressAccounts(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_getNumAccounts(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return wallet->numSubaddressAccounts(); return static_cast<jint>(wallet->numSubaddressAccounts());
} }
//virtual size_t numSubaddresses(uint32_t accountIndex) const = 0;
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getNumSubaddresses(JNIEnv *env, jobject instance,
jint accountIndex) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return static_cast<jint>(wallet->numSubaddresses(accountIndex));
}
//virtual void addSubaddress(uint32_t accountIndex, const std::string &label) = 0;
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_addSubaddress(JNIEnv *env, jobject instance,
jint accountIndex,
jstring label) {
const char *_label = env->GetStringUTFChars(label, NULL);
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
wallet->addSubaddress(accountIndex, _label);
env->ReleaseStringUTFChars(label, _label);
}
/*JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getLastSubaddress(JNIEnv *env, jobject instance,
jint accountIndex) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
size_t num = wallet->numSubaddresses(accountIndex);
//wallet->subaddress()->getAll()[num]->getAddress().c_str()
Monero::Subaddress *s = wallet->subaddress();
s->refresh(accountIndex);
std::vector<Monero::SubaddressRow *> v = s->getAll();
return env->NewStringUTF(v[num - 1]->getAddress().c_str());
}
*/
//virtual std::string signMessage(const std::string &message) = 0; //virtual std::string signMessage(const std::string &message) = 0;
//virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; //virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
@ -1079,10 +1112,13 @@ jobject newTransferList(JNIEnv *env, Bitmonero::TransactionInfo *info) {
jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>", jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>",
"(IZZJJJLjava/lang/String;JLjava/lang/String;IJLjava/util/List;)V"); "(IZZJJJLjava/lang/String;JLjava/lang/String;IIJLjava/util/List;)V");
jobject transfers = newTransferList(env, info); jobject transfers = newTransferList(env, info);
jstring _hash = env->NewStringUTF(info->hash().c_str()); jstring _hash = env->NewStringUTF(info->hash().c_str());
jstring _paymentId = env->NewStringUTF(info->paymentId().c_str()); jstring _paymentId = env->NewStringUTF(info->paymentId().c_str());
uint32_t subaddrIndex = 0;
if (info->direction() == Bitmonero::TransactionInfo::Direction_In)
subaddrIndex = *(info->subaddrIndex().begin());
jobject result = env->NewObject(class_TransactionInfo, c, jobject result = env->NewObject(class_TransactionInfo, c,
info->direction(), info->direction(),
info->isPending(), info->isPending(),
@ -1094,6 +1130,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
static_cast<jlong> (info->timestamp()), static_cast<jlong> (info->timestamp()),
_paymentId, _paymentId,
info->subaddrAccount(), info->subaddrAccount(),
subaddrIndex,
info->confirmations(), info->confirmations(),
transfers); transfers);
env->DeleteLocalRef(transfers); env->DeleteLocalRef(transfers);
@ -1104,6 +1141,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector) { jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector) {
jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V"); jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
@ -1161,11 +1199,13 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getAmount(JNIEnv *env, jobjec
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance); Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);
return tx->amount(); return 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) {
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance); Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);
return tx->dust(); return 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) {
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance); Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);

View File

@ -62,6 +62,7 @@ import timber.log.Timber;
public class ReceiveFragment extends Fragment { public class ReceiveFragment extends Fragment {
private ProgressBar pbProgress; private ProgressBar pbProgress;
private TextView tvAddressLabel;
private TextView tvAddress; private TextView tvAddress;
private TextInputLayout etPaymentId; private TextInputLayout etPaymentId;
private ExchangeView evAmount; private ExchangeView evAmount;
@ -71,6 +72,10 @@ public class ReceiveFragment extends Fragment {
private ImageView qrCodeFull; private ImageView qrCodeFull;
private EditText etDummy; private EditText etDummy;
private ImageButton bCopyAddress; private ImageButton bCopyAddress;
private Button bSubaddress;
private Wallet wallet = null;
private boolean isMyWallet = false;
public interface Listener { public interface Listener {
void setToolbarButton(int type); void setToolbarButton(int type);
@ -87,6 +92,7 @@ public class ReceiveFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_receive, container, false); View view = inflater.inflate(R.layout.fragment_receive, container, false);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress); pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel);
tvAddress = (TextView) view.findViewById(R.id.tvAddress); tvAddress = (TextView) view.findViewById(R.id.tvAddress);
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId); etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
evAmount = (ExchangeView) view.findViewById(R.id.evAmount); evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
@ -96,6 +102,7 @@ public class ReceiveFragment extends Fragment {
qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull); qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull);
etDummy = (EditText) view.findViewById(R.id.etDummy); etDummy = (EditText) view.findViewById(R.id.etDummy);
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress); bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
bSubaddress = (Button) view.findViewById(R.id.bSubaddress);
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
@ -162,6 +169,18 @@ public class ReceiveFragment extends Fragment {
} }
}); });
bSubaddress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
enableSubaddressButton(false);
tvAddress.setText(wallet.getNewSubaddress());
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
wallet.getNumSubaddresses() - 1));
storeWallet();
generateQr();
}
});
qrCode.setOnClickListener(new View.OnClickListener() { qrCode.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -194,11 +213,25 @@ public class ReceiveFragment extends Fragment {
String password = b.getString("password"); String password = b.getString("password");
loadAndShow(path, password); loadAndShow(path, password);
} else { } else {
show(walletName, address); if (getActivity() instanceof GenerateReviewFragment.ListenerWithWallet) {
wallet = ((GenerateReviewFragment.ListenerWithWallet) getActivity()).getWallet();
show();
} else {
throw new IllegalStateException("no wallet info");
}
} }
return view; return view;
} }
void enableSubaddressButton(boolean enable) {
bSubaddress.setEnabled(enable);
if (enable) {
bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_orange_24dp,0,0);
} else {
bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_gray_24dp,0,0);
}
}
void copyAddress() { void copyAddress() {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString()); Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
@ -228,17 +261,17 @@ public class ReceiveFragment extends Fragment {
super.onResume(); super.onResume();
Timber.d("onResume()"); Timber.d("onResume()");
listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK); listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK);
listenerCallback.setSubtitle(getString(R.string.receive_title)); listenerCallback.setSubtitle(wallet.getAccountLabel());
generateQr(); generateQr();
} }
private boolean isLoaded = false; private boolean isLoaded = false;
private void show(String name, String address) { private void show() {
Timber.d("name=%s", name); Timber.d("name=%s", wallet.getName());
isLoaded = true; isLoaded = true;
listenerCallback.setTitle(name); listenerCallback.setTitle(wallet.getName());
tvAddress.setText(address); tvAddress.setText(wallet.getAddress());
etPaymentId.setEnabled(true); etPaymentId.setEnabled(true);
bPaymentId.setEnabled(true); bPaymentId.setEnabled(true);
bCopyAddress.setClickable(true); bCopyAddress.setClickable(true);
@ -254,18 +287,14 @@ public class ReceiveFragment extends Fragment {
private class AsyncShow extends AsyncTask<String, Void, Boolean> { private class AsyncShow extends AsyncTask<String, Void, Boolean> {
String password; String password;
String address;
String name;
@Override @Override
protected Boolean doInBackground(String... params) { protected Boolean doInBackground(String... params) {
if (params.length != 2) return false; if (params.length != 2) return false;
String walletPath = params[0]; String walletPath = params[0];
password = params[1]; password = params[1];
Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); wallet = WalletManager.getInstance().openWallet(walletPath, password);
address = wallet.getAddress(); isMyWallet = true;
name = wallet.getName();
wallet.close();
return true; return true;
} }
@ -274,7 +303,7 @@ public class ReceiveFragment extends Fragment {
super.onPostExecute(result); super.onPostExecute(result);
if (!isAdded()) return; // never mind if (!isAdded()) return; // never mind
if (result) { if (result) {
show(name, address); show();
} else { } else {
Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show();
hideProgress(); hideProgress();
@ -282,6 +311,27 @@ public class ReceiveFragment extends Fragment {
} }
} }
private void storeWallet() {
new AsyncStore().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
}
private class AsyncStore extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
if (params.length != 0) return false;
if (wallet != null) wallet.store();
return true;
}
@Override
protected void onPostExecute(Boolean result) {
enableSubaddressButton(true);
super.onPostExecute(result);
}
}
private boolean checkPaymentId() { private boolean checkPaymentId() {
String paymentId = etPaymentId.getEditText().getText().toString(); String paymentId = etPaymentId.getEditText().getText().toString();
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId); boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
@ -418,4 +468,15 @@ public class ReceiveFragment extends Fragment {
Timber.d("onPause()"); Timber.d("onPause()");
super.onPause(); super.onPause();
} }
@Override
public void onDetach() {
Timber.d("onDetach()");
if ((wallet != null) && (isMyWallet)) {
wallet.close();
wallet = null;
isMyWallet = false;
}
super.onDetach();
}
} }

View File

@ -224,7 +224,7 @@ public class TxFragment extends Fragment {
activityCallback.setSubtitle(getString(R.string.tx_title)); activityCallback.setSubtitle(getString(R.string.tx_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK); activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
tvAccount.setText("" + info.subaddrAccount); tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress));
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000))); tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
tvTxId.setText(info.hash); tvTxId.setText(info.hash);
@ -287,7 +287,10 @@ public class TxFragment extends Fragment {
} }
} else { } else {
sb.append("-"); sb.append("-");
dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ? activityCallback.getWalletAddress() : "-"); dstSb.append(info.direction ==
TransactionInfo.Direction.Direction_In ?
activityCallback.getWalletSubaddress(info.account, info.subaddress) :
"-");
} }
tvTxTransfers.setText(sb.toString()); tvTxTransfers.setText(sb.toString());
tvDestination.setText(dstSb.toString()); tvDestination.setText(dstSb.toString());
@ -322,7 +325,7 @@ public class TxFragment extends Fragment {
Listener activityCallback; Listener activityCallback;
public interface Listener { public interface Listener {
String getWalletAddress(); String getWalletSubaddress(int accountIndex, int subaddressIndex);
String getTxKey(String hash); String getTxKey(String hash);

View File

@ -491,8 +491,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public boolean onRefreshed(final Wallet wallet, final boolean full) { public boolean onRefreshed(final Wallet wallet, final boolean full) {
Timber.d("onRefreshed()"); Timber.d("onRefreshed()");
if (numAccounts != wallet.numAccounts()) { if (numAccounts != wallet.getNumAccounts()) {
numAccounts = wallet.numAccounts(); numAccounts = wallet.getNumAccounts();
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
updateAccountsList(); updateAccountsList();
@ -740,8 +740,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
@Override @Override
public String getWalletAddress() { public String getWalletSubaddress(int accountIndex, int subaddressIndex) {
return getWallet().getAddress(); return getWallet().getSubaddress(accountIndex, subaddressIndex);
} }
public String getWalletName() { public String getWalletName() {
@ -891,7 +891,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public void onWalletReceive() { public void onWalletReceive() {
startReceive(getWalletAddress()); startReceive(getWallet().getAddress());
} }
void startReceive(String address) { void startReceive(String address) {
@ -966,7 +966,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
Helper.getDisplayAmount(wallet.getBalanceAll(), 5))); Helper.getDisplayAmount(wallet.getBalanceAll(), 5)));
Menu menu = accountsView.getMenu(); Menu menu = accountsView.getMenu();
menu.removeGroup(R.id.accounts_list); menu.removeGroup(R.id.accounts_list);
for (int i = 0; i < wallet.numAccounts(); i++) { final int n = wallet.getNumAccounts();
for (int i = 0; i < n; i++) {
final String label = wallet.getAccountLabel(i); final String label = wallet.getAccountLabel(i);
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label); final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp); item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
@ -1050,7 +1051,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
switch (id) { switch (id) {
case R.id.account_new: case R.id.account_new:
getWallet().addAccount(); getWallet().addAccount();
int newIdx = getWallet().numAccounts() - 1; int newIdx = getWallet().getNumAccounts() - 1;
getWallet().setAccountIndex(newIdx); getWallet().setAccountIndex(newIdx);
Toast.makeText(this, Toast.makeText(this,
getString(R.string.accounts_new, newIdx), getString(R.string.accounts_new, newIdx),

View File

@ -166,7 +166,11 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
} }
if ((userNotes.note.isEmpty())) { if ((userNotes.note.isEmpty())) {
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId); this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ?
(infoItem.subaddress != 0 ?
(context.getString(R.string.tx_subaddress, infoItem.subaddress)) :
"") :
infoItem.paymentId);
} else { } else {
this.tvPaymentId.setText(userNotes.note); this.tvPaymentId.setText(userNotes.note);
} }

View File

@ -75,7 +75,7 @@ public class TransactionHistory {
Timber.d("refreshed %d", t.size()); Timber.d("refreshed %d", t.size());
for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) { for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) {
TransactionInfo info = iterator.next(); TransactionInfo info = iterator.next();
if (info.subaddrAccount != accountIndex) { if (info.account != accountIndex) {
iterator.remove(); iterator.remove();
Timber.d("removed %s", info.hash); Timber.d("removed %s", info.hash);
} else { } else {

View File

@ -67,7 +67,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
public String hash; public String hash;
public long timestamp; public long timestamp;
public String paymentId; public String paymentId;
public int subaddrAccount; public int account;
public int subaddress;
public long confirmations; public long confirmations;
public List<Transfer> transfers; public List<Transfer> transfers;
@ -84,7 +85,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
String hash, String hash,
long timestamp, long timestamp,
String paymentId, String paymentId,
int subaddrAccount, int account,
int subaddress,
long confirmations, long confirmations,
List<Transfer> transfers) { List<Transfer> transfers) {
this.direction = Direction.values()[direction]; this.direction = Direction.values()[direction];
@ -96,7 +98,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
this.hash = hash; this.hash = hash;
this.timestamp = timestamp; this.timestamp = timestamp;
this.paymentId = paymentId; this.paymentId = paymentId;
this.subaddrAccount = subaddrAccount; this.account = account;
this.subaddress = subaddress;
this.confirmations = confirmations; this.confirmations = confirmations;
this.transfers = transfers; this.transfers = transfers;
} }
@ -116,7 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(hash); out.writeString(hash);
out.writeLong(timestamp); out.writeLong(timestamp);
out.writeString(paymentId); out.writeString(paymentId);
out.writeInt(subaddrAccount); out.writeInt(account);
out.writeInt(subaddress);
out.writeLong(confirmations); out.writeLong(confirmations);
out.writeList(transfers); out.writeList(transfers);
out.writeString(txKey); out.writeString(txKey);
@ -143,7 +147,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
hash = in.readString(); hash = in.readString();
timestamp = in.readLong(); timestamp = in.readLong();
paymentId = in.readString(); paymentId = in.readString();
subaddrAccount = in.readInt(); account = in.readInt();
subaddress = in.readInt();
confirmations = in.readLong(); confirmations = in.readLong();
transfers = in.readArrayList(Transfer.class.getClassLoader()); transfers = in.readArrayList(Transfer.class.getClassLoader());
txKey = in.readString(); txKey = in.readString();

View File

@ -19,6 +19,9 @@ package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import timber.log.Timber; import timber.log.Timber;
@ -86,10 +89,22 @@ public class Wallet {
public native boolean setPassword(String password); public native boolean setPassword(String password);
public String getAddress() { public String getAddress() {
return getAddressJ(accountIndex); return getAddress(accountIndex);
} }
private native String getAddressJ(int accountIndex); public String getAddress(int accountIndex) {
return getAddressJ(accountIndex, 0);
}
public String getSubaddress(int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
public String getSubaddress(int accountIndex, int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
private native String getAddressJ(int accountIndex, int addressIndex);
public native String getPath(); public native String getPath();
@ -109,7 +124,9 @@ public class Wallet {
public native String getSecretSpendKey(); public native String getSecretSpendKey();
public boolean store() { public boolean store() {
return store(""); final boolean ok = store("");
Timber.d("stored");
return ok;
} }
public native boolean store(String path); public native boolean store(String path);
@ -323,17 +340,21 @@ public class Wallet {
public String getAccountLabel(int accountIndex) { public String getAccountLabel(int accountIndex) {
String label = getSubaddressLabel(accountIndex, 0); String label = getSubaddressLabel(accountIndex, 0);
if (label.equals(NEW_ACCOUNT_NAME)) { if (label.equals(NEW_ACCOUNT_NAME)) {
String address = getAddressJ(accountIndex); String address = getAddress(accountIndex);
int len = address.length(); int len = address.length();
return address.substring(0, 6) + return address.substring(0, 6) +
"\u2026" + address.substring(len - 6, len); "\u2026" + address.substring(len - 6, len);
} else return label; } else return label;
} }
public String getSubaddressLabel(int addressIndex) {
return getSubaddressLabel(accountIndex, addressIndex);
}
public native String getSubaddressLabel(int accountIndex, int addressIndex); public native String getSubaddressLabel(int accountIndex, int addressIndex);
public void setAccountLabel(String label) { public void setAccountLabel(String label) {
setSubaddressLabel(accountIndex, 0, label); setAccountLabel(accountIndex, label);
} }
public void setAccountLabel(int accountIndex, String label) { public void setAccountLabel(int accountIndex, String label) {
@ -342,9 +363,30 @@ public class Wallet {
public native void setSubaddressLabel(int accountIndex, int addressIndex, String label); public native void setSubaddressLabel(int accountIndex, int addressIndex, String label);
public int numAccounts() { public native int getNumAccounts();
return numSubaddressAccounts();
public int getNumSubaddresses() {
return getNumSubaddresses(accountIndex);
}
public native int getNumSubaddresses(int accountIndex);
public String getNewSubaddress() {
return getNewSubaddress(accountIndex);
}
public String getNewSubaddress(int accountIndex) {
String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).format(new Date());
addSubaddress(accountIndex, timeStamp);
String subaddress = getLastSubaddress(accountIndex);
Timber.d("%d: %s", getNumSubaddresses(accountIndex) - 1, subaddress);
return subaddress;
}
public native void addSubaddress(int accountIndex, String label);
public String getLastSubaddress(int accountIndex) {
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
} }
public native int numSubaddressAccounts();
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="@color/moneroGray"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z" />
</vector>

View File

@ -24,11 +24,13 @@
android:indeterminate="true" android:indeterminate="true"
android:visibility="gone" /> android:visibility="gone" />
<FrameLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:gravity="center">
<TextView <TextView
android:id="@+id/tvAddressLabel"
style="@style/MoneroLabel.Heading" style="@style/MoneroLabel.Heading"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -39,21 +41,45 @@
android:id="@+id/bCopyAddress" android:id="@+id/bCopyAddress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_marginStart="8dp"
android:layout_marginEnd="80dp"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:clickable="false" android:clickable="false"
android:src="@drawable/ic_content_nocopy_black_24dp" /> android:src="@drawable/ic_content_nocopy_black_24dp" />
</FrameLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:weightSum="10">
<TextView <TextView
android:id="@+id/tvAddress" android:id="@+id/tvAddress"
style="@style/MoneroText.Medium" style="@style/MoneroText.Medium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="10"
android:textAlignment="center" android:textAlignment="center"
tools:text="9wZnnNctRc7RaLya1rxykH21dUwfQpNGmVLjAvkvqe7nKT2Mw848AJNGMunW5xjoSZ5vCCU3uDnUoVqSSHxzRtQBE3f6crx" /> tools:text="9wZnnNctRc7RaLya1rxykH21dUwfQpNGmVLjAvkvqe7nKT2Mw848AJNGMunW5xjoSZ5vCCU3uDnUoVqSSHxzRtQBE3f6crx" />
<Button
android:id="@+id/bSubaddress"
style="@style/MoneroText.Button.Small"
android:layout_width="56dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_weight="0"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:drawableTop="@drawable/ic_settings_orange_24dp"
android:text="@string/send_generate_paymentid_hint"
android:textColor="@color/moneroGray"
android:visibility="visible" />
</LinearLayout>
<com.m2049r.xmrwallet.widget.ExchangeView <com.m2049r.xmrwallet.widget.ExchangeView
android:id="@+id/evAmount" android:id="@+id/evAmount"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -96,7 +122,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_weight="0" android:layout_weight="0"
android:background="?android:selectableItemBackground" android:background="?android:attr/selectableItemBackgroundBorderless"
android:drawableTop="@drawable/ic_settings_orange_24dp" android:drawableTop="@drawable/ic_settings_orange_24dp"
android:text="@string/send_generate_paymentid_hint" android:text="@string/send_generate_paymentid_hint"
android:textColor="@color/moneroGray" android:textColor="@color/moneroGray"
@ -122,10 +148,10 @@
android:visibility="invisible" /> android:visibility="invisible" />
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_margin="16dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="16dp"
android:clickable="true" android:clickable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
card_view:cardCornerRadius="2dp" card_view:cardCornerRadius="2dp"

View File

@ -318,4 +318,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -303,4 +303,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -321,4 +321,6 @@
<string name="accounts_new">Nouveau Compte #%1$d ajouté</string> <string name="accounts_new">Nouveau Compte #%1$d ajouté</string>
<string name="tx_account">Compte #</string> <string name="tx_account">Compte #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -319,4 +319,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -317,4 +317,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -315,4 +315,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -316,4 +316,6 @@
<string name="accounts_new">Added new account #%1$d</string> <string name="accounts_new">Added new account #%1$d</string>
<string name="tx_account">Account #</string> <string name="tx_account">Account #</string>
<string name="send_sweepall">Send all confirmed funds in this account!</string> <string name="send_sweepall">Send all confirmed funds in this account!</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
</resources> </resources>

View File

@ -216,6 +216,7 @@
<string name="generate_mnemonic_hint">25-Word Mnemonic Seed</string> <string name="generate_mnemonic_hint">25-Word Mnemonic Seed</string>
<string name="generate_restoreheight_hint">Restore Height or Date (YYYY-MM-DD)</string> <string name="generate_restoreheight_hint">Restore Height or Date (YYYY-MM-DD)</string>
<string name="generate_address_label_sub">Public Subaddress #%1$d</string>
<string name="generate_address_label">Public Address</string> <string name="generate_address_label">Public Address</string>
<string name="generate_viewkey_label">View Key</string> <string name="generate_viewkey_label">View Key</string>
<string name="generate_spendkey_label">Spend Key</string> <string name="generate_spendkey_label">Spend Key</string>
@ -365,4 +366,6 @@
<string name="accounts_drawer_new">Create Account</string> <string name="accounts_drawer_new">Create Account</string>
<string name="accounts_drawer_title">Accounts</string> <string name="accounts_drawer_title">Accounts</string>
<string name="tx_subaddress">Subaddress #%1$d</string>
<string name="tx_account_formatted" translatable="false">(%1$d, %2$d)</string>
</resources> </resources>