tx key + tweaks

This commit is contained in:
m2049r 2017-08-19 16:20:10 +02:00
parent 40da44222e
commit 408b1a68d0
16 changed files with 326 additions and 305 deletions

View File

@ -489,15 +489,13 @@ Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instan
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getSeed(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
const char *address = wallet->seed().c_str();
return env->NewStringUTF(address);
return env->NewStringUTF(wallet->seed().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getSeedLanguage(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
const char *address = wallet->getSeedLanguage().c_str();
return env->NewStringUTF(address);
return env->NewStringUTF(wallet->getSeedLanguage().c_str());
}
JNIEXPORT void JNICALL
@ -541,8 +539,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getAddress(JNIEnv *env, jobject instance)
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getPath(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
const char *path = wallet->path().c_str();
return env->NewStringUTF(path);
return env->NewStringUTF(wallet->path().c_str());
}
JNIEXPORT jboolean JNICALL
@ -839,7 +836,21 @@ Java_com_m2049r_xmrwallet_model_Wallet_setDefaultMixin(JNIEnv *env, jobject inst
//virtual bool setUserNote(const std::string &txid, const std::string &note) = 0;
//virtual std::string getUserNote(const std::string &txid) const = 0;
//virtual std::string getTxKey(const std::string &txid) const = 0;
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getTxKey(JNIEnv *env, jobject instance,
jstring txid) {
const char *_txid = env->GetStringUTFChars(txid, JNI_FALSE);
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
std::string txKey = wallet->getTxKey(_txid);
env->ReleaseStringUTFChars(txid, _txid);
return env->NewStringUTF(txKey.c_str());
}
//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;
@ -856,28 +867,6 @@ Java_com_m2049r_xmrwallet_model_TransactionHistory_getCount(JNIEnv *env, jobject
return history->count();
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_getTransactionByIndexJ(JNIEnv *env,
jobject instance,
jint i) {
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance);
Bitmonero::TransactionInfo *info = history->transaction(i);
return reinterpret_cast<jlong>(info);
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_getTransactionByIdJ(JNIEnv *env,
jobject instance,
jstring id) {
const char *_id = env->GetStringUTFChars(id, JNI_FALSE);
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance);
Bitmonero::TransactionInfo *info = history->transaction(std::string(_id));
env->ReleaseStringUTFChars(id, _id);
return reinterpret_cast<jlong>(info);
}
jobject newTransferInstance(JNIEnv *env, uint64_t amount, const std::string &address) {
jmethodID c = env->GetMethodID(class_Transfer, "<init>",
"(JLjava/lang/String;)V");
@ -896,7 +885,6 @@ jobject newTransferList(JNIEnv *env, Bitmonero::TransactionInfo *info) {
"(Ljava/lang/Object;)Z");
jobject result = env->NewObject(class_ArrayList, java_util_ArrayList_, transfers.size());
// create Transfer objects and stick them in the List
LOGD("size %i", transfers.size());
for (const Bitmonero::TransactionInfo::Transfer &s: transfers) {
jobject element = newTransferInstance(env, s.amount, s.address);
env->CallBooleanMethod(result, java_util_ArrayList_add, element);
@ -908,8 +896,6 @@ jobject newTransferList(JNIEnv *env, Bitmonero::TransactionInfo *info) {
jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>",
"(IZZJJJLjava/lang/String;JLjava/lang/String;JLjava/util/List;)V");
//"(IZZJJJLjava/lang/String;JLjava/lang/String;J)V");
LOGD("newTransactionInfo %s", info->hash().c_str());
jobject transfers = newTransferList(env, info);
jstring _hash = env->NewStringUTF(info->hash().c_str());
jstring _paymentId = env->NewStringUTF(info->paymentId().c_str());
@ -928,7 +914,6 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
env->DeleteLocalRef(transfers);
env->DeleteLocalRef(_hash);
env->DeleteLocalRef(_paymentId);
LOGD("newTransactionInfo X");
return result;
}
@ -940,9 +925,11 @@ jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector)
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
"(Ljava/lang/Object;)Z");
LOGD("%s", std::to_string(vector.size()).c_str());
jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, vector.size());
for (Bitmonero::TransactionInfo *s: vector) {
if (s->fee()>1) {
LOGE("TX %s %" PRIu64 " %" PRIu64, s->hash().c_str(), s->fee(), s->amount());
}
jobject info = newTransactionInfo(env, s);
env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info);
env->DeleteLocalRef(info);
@ -954,79 +941,11 @@ JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_refreshJ(JNIEnv *env, jobject instance) {
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance);
LOGD("history->refresh()");
history->refresh();
LOGD("history->refresh() done");
return cpp2java(env, history->getAll());
}
// TransactionInfo
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getDirectionJ(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->direction();
}
JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_isPending(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->isPending();
}
JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_isFailed(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->isFailed();
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getAmount(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->amount();
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getFee(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->fee();
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getBlockHeight(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->blockHeight();
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getConfirmations(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->confirmations();
}
//TODO remove all these
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getHash(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return env->NewStringUTF(info->hash().c_str());
}
JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getTimestamp(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->timestamp();
}
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getPaymentId(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return env->NewStringUTF(info->paymentId().c_str());
}
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransferCount(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->transfers().size();
}
// TransactionInfo is implemented in Java - no need here
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getStatusJ(JNIEnv *env, jobject instance) {
@ -1071,7 +990,7 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject i
return tx->fee();
}
/* this returns a vector of strings - deal with this later
/* TODO this returns a vector of strings - deal with this later
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxId(JNIEnv *env, jobject instance) {
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);

View File

@ -73,9 +73,8 @@ public class GenerateFragment extends Fragment {
etWalletSpendKey.setRawInputType(InputType.TYPE_CLASS_TEXT);
boolean testnet = WalletManager.getInstance().isTestNet();
etWalletMnemonic.setTextIsSelectable(testnet);
//etWalletMnemonic.setTextIsSelectable(testnet);
etWalletName.requestFocus();
Helper.showKeyboard(getActivity());
etWalletName.addTextChangedListener(new TextWatcher() {
@Override
@ -95,12 +94,6 @@ public class GenerateFragment extends Fragment {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
etWalletName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
@ -113,12 +106,6 @@ public class GenerateFragment extends Fragment {
}
});
etWalletPassword.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
@ -148,14 +135,6 @@ public class GenerateFragment extends Fragment {
return false;
}
});
etWalletMnemonic.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletMnemonic.addTextChangedListener(new
TextWatcher() {
@ -204,14 +183,6 @@ public class GenerateFragment extends Fragment {
return false;
}
});
etWalletAddress.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletAddress.addTextChangedListener(new
TextWatcher() {
@ -256,15 +227,6 @@ public class GenerateFragment extends Fragment {
return false;
}
});
etWalletViewKey.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
@ -280,23 +242,16 @@ public class GenerateFragment extends Fragment {
return false;
}
});
etWalletSpendKey.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
if (bGenerate.getVisibility() == View.VISIBLE) {
Helper.hideKeyboard(getActivity());
generateWallet();
} else {
Toast.makeText(getActivity(), getString(R.string.generate_check_something), Toast.LENGTH_LONG).show();
}
return true;
}
@ -314,6 +269,7 @@ public class GenerateFragment extends Fragment {
}
});
etWalletName.requestFocus();
return view;
}

View File

@ -23,17 +23,21 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
import java.io.File;
public class GenerateReviewFragment extends Fragment {
static final String TAG = "GenerateReviewFragment";
static final public String VIEW_DETAILS = "details";
static final public String VIEW_ACCEPT = "accept";
ProgressBar pbProgress;
TextView tvWalletName;
TextView tvWalletPassword;
TextView tvWalletAddress;
@ -48,6 +52,7 @@ public class GenerateReviewFragment extends Fragment {
View view = inflater.inflate(R.layout.gen_review_fragment, container, false);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
tvWalletName = (TextView) view.findViewById(R.id.tvWalletName);
tvWalletPassword = (TextView) view.findViewById(R.id.tvWalletPassword);
tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress);
@ -67,10 +72,13 @@ public class GenerateReviewFragment extends Fragment {
}
});
showProgress();
Bundle b = getArguments();
String name = b.getString("name");
String password = b.getString("password");
String type = b.getString("type");
tvWalletName.setText(new File(name).getName());
show(name, password, type);
return view;
@ -112,6 +120,7 @@ public class GenerateReviewFragment extends Fragment {
} else {
tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly));
}
hideProgress();
}
});
}
@ -136,4 +145,13 @@ public class GenerateReviewFragment extends Fragment {
+ " must implement Listener");
}
}
public void showProgress() {
pbProgress.setIndeterminate(true);
pbProgress.setVisibility(View.VISIBLE);
}
public void hideProgress() {
pbProgress.setVisibility(View.INVISIBLE);
}
}

View File

@ -98,7 +98,7 @@ public class LoginFragment extends Fragment {
@Override
public void onPause() {
Log.d(TAG, "onPause()");
savePrefs(false);
savePrefs();
super.onPause();
}
@ -184,7 +184,7 @@ public class LoginFragment extends Fragment {
}
// looking good
savePrefs(false);
savePrefs();
String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH);
if (itemValue.charAt(1) == '-') wallet = ':' + wallet;
@ -195,14 +195,7 @@ public class LoginFragment extends Fragment {
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
EditText tvDaemonAddress = (EditText) getView().findViewById(R.id.etDaemonAddress);
if (tvDaemonAddress.getText().toString().length() == 0) {
Toast.makeText(getActivity(), getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show();
tvDaemonAddress.requestFocus();
Helper.showKeyboard(getActivity());
return true;
}
// Difference to opening wallet is that we don't need a daemon set
String itemValue = (String) listView.getItemAtPosition(position);
if (itemValue.length() <= (WALLETNAME_PREAMBLE_LENGTH)) {
@ -210,26 +203,28 @@ public class LoginFragment extends Fragment {
return true;
}
String x = isMainNet() ? "4-" : "9A-";
String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH);
if (itemValue.charAt(1) == '-') {
Toast.makeText(getActivity(), getString(R.string.bad_wallet), Toast.LENGTH_LONG).show();
return true;
}
String x = isMainNet() ? "4" : "9A";
if (x.indexOf(itemValue.charAt(1)) < 0) {
Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show();
return true;
}
if (!checkAndSetWalletDaemon(getDaemon(), !isMainNet())) {
if (!checkAndSetWalletDaemon("", !isMainNet())) {
Toast.makeText(getActivity(), getString(R.string.warn_daemon_unavailable), Toast.LENGTH_SHORT).show();
return true;
}
// looking good
savePrefs(false);
String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH);
if (itemValue.charAt(1) == '-') wallet = ':' + wallet;
activityCallback.onWalletDetails(wallet);
return true;
}
});
activityCallback.setTitle(getString(R.string.app_name) + " " +
getString(isMainNet() ? R.string.connect_mainnet : R.string.connect_testnet));
@ -305,6 +300,10 @@ public class LoginFragment extends Fragment {
}
}
void savePrefs() {
savePrefs(false);
}
void savePrefs(boolean usePreviousState) {
// save the daemon address for the net
boolean mainnet = isMainNet() ^ usePreviousState;
@ -324,42 +323,47 @@ public class LoginFragment extends Fragment {
}
private boolean checkAndSetWalletDaemon(String daemonAddress, boolean testnet) {
String d[] = daemonAddress.split(":");
if (d.length > 2) return false;
if (d.length < 1) return false;
String host = d[0];
int port;
if (d.length == 2) {
try {
port = Integer.parseInt(d[1]);
} catch (NumberFormatException ex) {
return false;
if (!daemonAddress.isEmpty()) {
String d[] = daemonAddress.split(":");
if (d.length > 2) return false;
if (d.length < 1) return false;
String host = d[0];
int port;
if (d.length == 2) {
try {
port = Integer.parseInt(d[1]);
} catch (NumberFormatException ex) {
return false;
}
} else {
port = (testnet ? 28081 : 18081);
}
} else {
port = (testnet ? 28081 : 18081);
}
// if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy prevPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder(prevPolicy).permitNetwork().build();
StrictMode.setThreadPolicy(policy);
Socket socket = new Socket();
long a = new Date().getTime();
try {
socket.connect(new InetSocketAddress(host, port), LoginActivity.DAEMON_TIMEOUT);
socket.close();
} catch (IOException ex) {
Log.d(TAG, "Cannot reach daemon " + host + ":" + port + " because " + ex.getLocalizedMessage());
return false;
} finally {
StrictMode.setThreadPolicy(prevPolicy);
StrictMode.ThreadPolicy prevPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder(prevPolicy).permitNetwork().build();
StrictMode.setThreadPolicy(policy);
Socket socket = new Socket();
long a = new Date().getTime();
try {
socket.connect(new InetSocketAddress(host, port), LoginActivity.DAEMON_TIMEOUT);
socket.close();
} catch (IOException ex) {
Log.d(TAG, "Cannot reach daemon " + host + ":" + port + " because " + ex.getLocalizedMessage());
return false;
} finally {
StrictMode.setThreadPolicy(prevPolicy);
}
long b = new Date().getTime();
Log.d(TAG, "Daemon is " + (b - a) + "ms away.");
}
long b = new Date().getTime();
Log.d(TAG, "Daemon is " + (b - a) + "ms away.");
WalletManager mgr = WalletManager.getInstance();
mgr.setDaemon(daemonAddress, testnet);
int version = mgr.getDaemonVersion();
Log.d(TAG, "Daemon is v" + version);
return (version >= WalletActivity.MIN_DAEMON_VERSION);
if (!daemonAddress.isEmpty()) {
int version = mgr.getDaemonVersion();
Log.d(TAG, "Daemon is v" + version);
return (version >= WalletActivity.MIN_DAEMON_VERSION);
} else {
return true;
}
}
}

View File

@ -19,7 +19,9 @@ package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@ -29,6 +31,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
@ -53,6 +56,7 @@ public class SendFragment extends Fragment {
TextView tvTxFee;
TextView tvTxDust;
Button bSend;
ProgressBar pbProgress;
final static int Mixins[] = {4, 6, 8, 10, 13}; // must match the layout XML
final static PendingTransaction.Priority Priorities[] =
@ -80,6 +84,8 @@ public class SendFragment extends Fragment {
tvTxDust = (TextView) view.findViewById(R.id.tvTxDust);
bSend = (Button) view.findViewById(R.id.bSend);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
etAddress.setRawInputType(InputType.TYPE_CLASS_TEXT);
etPaymentId.setRawInputType(InputType.TYPE_CLASS_TEXT);
@ -100,11 +106,22 @@ public class SendFragment extends Fragment {
return false;
}
});
etPaymentId.setOnClickListener(new View.OnClickListener() {
etAddress.addTextChangedListener(new TextWatcher() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
public void afterTextChanged(Editable editable) {
if (addressOk() && amountOk()) {
bPrepareSend.setEnabled(true);
} else {
bPrepareSend.setEnabled(false);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
etPaymentId.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@ -130,8 +147,26 @@ public class SendFragment extends Fragment {
return false;
}
});
etAmount.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
if (addressOk() && amountOk()) {
bPrepareSend.setEnabled(true);
} else {
bPrepareSend.setEnabled(false);
}
}
bPrepareSend.setEnabled(true); // TODO need clever logic here
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
setPrepareButtonState();
bPrepareSend.setOnClickListener(new View.OnClickListener()
{
@ -154,16 +189,26 @@ public class SendFragment extends Fragment {
return view;
}
private void setPrepareButtonState() {
if (addressOk() && amountOk() && (bSend.getVisibility() != View.VISIBLE)) {
bPrepareSend.setEnabled(true);
} else {
bPrepareSend.setEnabled(false);
}
}
private boolean addressOk() {
String address = etAddress.getText().toString();
// TODO only accept address from the correct net
return ((address.length() == 95) && ("49A".indexOf(address.charAt(0)) >= 0));
if (WalletManager.getInstance().isTestNet()) {
return ((address.length() == 95) && ("9A".indexOf(address.charAt(0)) >= 0));
} else {
return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0));
}
}
private boolean amountOk() {
String amount = etAmount.getText().toString();
// TODO decimal separator
return ((amount.length() > 0) && (amount.matches("^[0-9]+([,.][0-9]+)?$")));
long amount = Wallet.getAmountFromString(etAmount.getText().toString());
return (amount > 0);
}
private boolean paymentIdOk() {
@ -186,6 +231,12 @@ public class SendFragment extends Fragment {
mixin,
priority);
disableEdit();
showProgress();
activityCallback.onPrepareSend(txData);
}
private void disableEdit() {
sMixin.setEnabled(false);
sPriority.setEnabled(false);
etAddress.setEnabled(false);
@ -193,8 +244,16 @@ public class SendFragment extends Fragment {
etAmount.setEnabled(false);
bSweep.setEnabled(false);
bPrepareSend.setEnabled(false);
}
activityCallback.onPrepareSend(txData);
private void enableEdit() {
sMixin.setEnabled(true);
sPriority.setEnabled(true);
etAddress.setEnabled(true);
etPaymentId.setEnabled(true);
etAmount.setEnabled(true);
bSweep.setEnabled(true);
bPrepareSend.setEnabled(true);
}
private void send() {
@ -222,20 +281,24 @@ public class SendFragment extends Fragment {
}
public void onCreatedTransaction(PendingTransaction pendingTransaction) {
PendingTransaction.Status status = pendingTransaction.getStatus();
if (status != PendingTransaction.Status.Status_Ok) {
Log.d(TAG, "Wallet store failed: " + pendingTransaction.getErrorString());
hideProgress();
if (pendingTransaction == null) {
enableEdit();
return;
}
/*
Log.d(TAG, "transaction amount " + pendingTransaction.getAmount());
Log.d(TAG, "transaction fee " + pendingTransaction.getFee());
Log.d(TAG, "transaction dust " + pendingTransaction.getDust());
Log.d(TAG, "transactions " + pendingTransaction.getTxCount());
*/
llConfirmSend.setVisibility(View.VISIBLE);
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount()));
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
tvTxDust.setText(Wallet.getDisplayAmount(pendingTransaction.getDust()));
bSend.setEnabled(true);
}
public void showProgress() {
pbProgress.setIndeterminate(true);
pbProgress.setVisibility(View.VISIBLE);
}
public void hideProgress() {
pbProgress.setVisibility(View.GONE);
}
}

View File

@ -47,10 +47,21 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
private boolean synced = false;
@Override
public boolean isSynced() {
return synced;
}
@Override
public boolean isWatchOnly() {
return getWallet().isWatchOnly();
}
@Override
public String getTxKey(String txId) {
return getWallet().getTxKey(txId);
}
@Override
protected void onStart() {
super.onStart();
@ -99,7 +110,6 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
.add(R.id.fragment_container, walletFragment).commit();
Log.d(TAG, "fragment added");
// TODO do stuff with savedInstanceState ?
if (savedInstanceState != null) {
return;
}
@ -171,7 +181,6 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
@Override
protected void onPause() {
Log.d(TAG, "onPause()");
//saveWallet(); //TODO: do it here if we really need to ...
super.onPause();
}
@ -260,7 +269,6 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
@Override
public boolean onRefreshed(final Wallet wallet, final boolean full) {
Log.d(TAG, "onRefreshed()");
// TODO check which fragment is loaded
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
@ -295,7 +303,6 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
runOnUiThread(new Runnable() {
public void run() {
if (success) {
// TODO signal so we can show/enable send button
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unloaded), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unload_failed), Toast.LENGTH_LONG).show();
@ -306,14 +313,28 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
@Override
public void onCreatedTransaction(final PendingTransaction pendingTransaction) {
// TODO check which fragment is loaded
final SendFragment sendFragment = (SendFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
sendFragment.onCreatedTransaction(pendingTransaction);
}
});
final PendingTransaction.Status status = pendingTransaction.getStatus();
if (status != PendingTransaction.Status.Status_Ok) {
getWallet().disposePendingTransaction();
}
try {
final SendFragment sendFragment = (SendFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
if (status != PendingTransaction.Status.Status_Ok) {
Toast.makeText(WalletActivity.this, getString(R.string.status_transaction_prepare_failed), Toast.LENGTH_LONG).show();
sendFragment.onCreatedTransaction(null);
} else {
sendFragment.onCreatedTransaction(pendingTransaction);
}
}
});
} catch (ClassCastException ex) {
// not in spend fragment
// don't need the transaction any more
getWallet().disposePendingTransaction();
}
}
@Override
@ -332,27 +353,34 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
@Override
public void onProgress(final String text) {
//Log.d(TAG, "PROGRESS: " + text);
// TODO check which fragment is loaded
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(text);
}
});
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(text);
}
});
} catch (ClassCastException ex) {
// not in wallet fragment (probably send monero)
// keep calm and carry on
}
}
@Override
public void onProgress(final int n) {
// TODO check which fragment is loaded
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(n);
}
});
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(n);
}
});
} catch (ClassCastException ex) {
// not in wallet fragment (probably send monero)
// keep calm and carry on
}
}
private void updateProgress() {

View File

@ -99,7 +99,6 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
return view;
}
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
@ -107,23 +106,37 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("Transaction details");
builder.setNegativeButton("Copy TX ID", new DialogInterface.OnClickListener() {
infoItem.txKey = activityCallback.getTxKey(infoItem.hash);
builder.setPositiveButton("Copy TX ID", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ClipboardManager clipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("TX", infoItem.hash);
ClipData clip = ClipData.newPlainText("TXID", infoItem.hash);
clipboardManager.setPrimaryClip(clip);
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
if (!infoItem.txKey.isEmpty()) {
builder.setNegativeButton("Copy TX Key", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ClipboardManager clipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("TXKEY", infoItem.txKey);
clipboardManager.setPrimaryClip(clip);
}
});
}
// TODO use strings.xml
StringBuffer sb = new StringBuffer();
sb.append("TX ID: ").append(infoItem.hash);
sb.append("TX ID: ").append(infoItem.hash);
sb.append("\nTX Key: ");
if (!infoItem.txKey.isEmpty()) {
sb.append(infoItem.txKey);
} else {
sb.append(" -");
}
sb.append("\nPayment ID: ").append(infoItem.paymentId);
sb.append("\nBlockHeight: ").append(infoItem.blockheight);
sb.append("\nAmount: ");
@ -137,7 +150,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
sb.append(Wallet.getDisplayAmount(transfer.amount));
}
} else {
sb.append("-");
sb.append(" -");
}
builder.setMessage(sb.toString());
AlertDialog alert1 = builder.create();
@ -156,14 +169,16 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
}
public void onSynced() { // TODO watchonly
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
if (!activityCallback.isWatchOnly()) {
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
}
public void onProgress(final String text) {
if (text != null) {
tvProgress.setText(text);
showProgress(); //TODO optimize this
showProgress();
} else {
hideProgress();
tvProgress.setText(getString(R.string.status_working));
@ -194,7 +209,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
if (shortName.length() > 16) {
shortName = shortName.substring(0, 14) + "...";
}
String title = "[" + wallet.getAddress().substring(0, 6) + "] " + shortName;
String title = (wallet.isWatchOnly() ? "X " : "") + "[" + wallet.getAddress().substring(0, 6) + "] " + shortName;
activityCallback.setTitle(title);
Log.d(TAG, "wallet title is " + title);
return title;
@ -253,6 +268,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
void onSendRequest();
boolean isSynced();
boolean isWatchOnly();
String getTxKey(String txId);
}
@Override

View File

@ -30,27 +30,6 @@ public class TransactionHistory {
this.handle = handle;
}
/*
public TransactionInfo getTransaction(int i) {
long infoHandle = getTransactionByIndexJ(i);
return new TransactionInfo(infoHandle);
}
public TransactionInfo getTransaction(String id) {
long infoHandle = getTransactionByIdJ(id);
return new TransactionInfo(infoHandle);
}
*/
/*
public List<TransactionInfo> getAll() {
List<Long> handles = getAllJ();
List<TransactionInfo> infoList = new ArrayList<TransactionInfo>();
for (Long handle : handles) {
infoList.add(new TransactionInfo(handle.longValue()));
}
return infoList;
}
*/
public native int getCount();
//private native long getTransactionByIndexJ(int i);

View File

@ -40,6 +40,8 @@ public class TransactionInfo {
public long confirmations;
public List<Transfer> transfers;
public String txKey;
public TransactionInfo(
int direction,
boolean isPending,

View File

@ -23,8 +23,6 @@ public class Transfer {
public String address;
public Transfer(long amount, String address) {
Log.d("Transfer", address + "/" + amount);
//Log.d("Transfer", "/" + amount);
this.amount = amount;
this.address = address;
}

View File

@ -204,11 +204,8 @@ public class Wallet {
private TransactionHistory history = null;
public TransactionHistory getHistory() {
Log.d(TAG, "A");
if (history == null) {
Log.d(TAG, "B");
history = new TransactionHistory(getHistoryJ());
Log.d(TAG, "C");
}
return history;
}
@ -228,9 +225,9 @@ public class Wallet {
public native void setDefaultMixin(int mixin);
//virtual bool setUserNote(const std::string &txid, const std::string &note) = 0;
//virtual bool setUserNote(const std::string &txid, const std::string &note) = 0;
//virtual std::string getUserNote(const std::string &txid) const = 0;
//virtual std::string getTxKey(const std::string &txid) const = 0;
public native String getTxKey(String txid);
//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;

View File

@ -290,8 +290,11 @@ public class WalletService extends Service {
if (status != PendingTransaction.Status.Status_Ok) {
Log.d(TAG, "Create Transaction failed: " + pendingTransaction.getErrorString());
}
// TODO myWallet.disposeTransaction(pendingTransaction); later
if (observer != null) observer.onCreatedTransaction(pendingTransaction);
if (observer != null) {
observer.onCreatedTransaction(pendingTransaction);
} else {
myWallet.disposePendingTransaction();
}
} else if (cmd.equals(REQUEST_CMD_SEND)) {
Wallet myWallet = getWallet();
Log.d(TAG, "SEND TX for wallet: " + myWallet.getName());
@ -304,6 +307,15 @@ public class WalletService extends Service {
boolean success = pendingTransaction.commit("", true);
myWallet.disposePendingTransaction();
if (observer != null) observer.onSentTransaction(success);
if (success) {
boolean rc = myWallet.store();
Log.d(TAG, "wallet stored: " + myWallet.getName() + " with rc=" + rc);
if (!rc) {
Log.d(TAG, "Wallet store failed: " + myWallet.getErrorString());
}
if (observer != null) observer.onWalletStored(rc);
}
}
}
break;

View File

@ -88,7 +88,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/generate_restoreheight_hint"
android:imeOptions="actionNext"
android:imeOptions="actionDone"
android:inputType="number"
android:textAlignment="center"
android:textSize="16sp"

View File

@ -58,6 +58,20 @@
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<ProgressBar
android:id="@+id/pbProgress"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="invisible" />
</LinearLayout>
<TextView
android:id="@+id/tvWalletMnemonicLabel"
android:layout_width="match_parent"

View File

@ -106,6 +106,13 @@
android:enabled="false"
android:text="@string/send_prepare_hint" />
<ProgressBar
android:id="@+id/pbProgress"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="fill_parent"
android:visibility="gone"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/llConfirmSend"
android:layout_width="match_parent"
@ -123,8 +130,8 @@
android:id="@+id/tvTxAmountLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/send_amount_label"
android:textAlignment="textEnd"
android:textColor="@color/colorAccent"
@ -134,8 +141,8 @@
android:id="@+id/tvTxAmount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_marginLeft="8dp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
</LinearLayout>
@ -150,8 +157,8 @@
android:id="@+id/tvTxFeeLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/send_fee_label"
android:textAlignment="textEnd"
android:textColor="@color/colorAccent"
@ -161,8 +168,8 @@
android:id="@+id/tvTxFee"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_marginLeft="8dp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
</LinearLayout>
@ -177,22 +184,24 @@
android:id="@+id/tvTxDustLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/send_dust_label"
android:textAlignment="textEnd"
android:textColor="@color/colorAccent"
android:textSize="20sp" />
c
c
<TextView
android:id="@+id/tvTxDust"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_marginLeft="8dp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
</LinearLayout>
<Button
android:id="@+id/bSend"
android:layout_width="match_parent"

View File

@ -17,6 +17,7 @@
<string name="status_transaction_sent">Transaction sent!</string>
<string name="status_transaction_failed">Transaction failed!</string>
<string name="status_transaction_prepare_failed">Could not create transaction!</string>
<string name="prompt_password">Password for</string>
<string name="bad_password">Bad password!</string>
@ -56,8 +57,7 @@
<string name="generate_password_hint">Wallet Password</string>
<string name="generate_buttonGenerate">Do it already!</string>
<string name="generate_seed">Mnemonic Seed</string>
<string name="generate_button_accept">I have noted the mnemonic seed\nNow, I want to loose all my money!</string>
<string name="generate_button_reset">I\'m confused - Let me start again!</string>
<string name="generate_button_accept">I have noted the above\nNow, I want to loose all my money!</string>
<string name="generate_wallet_watchonly">&lt;Watch Only Wallet&gt;</string>
@ -103,9 +103,12 @@
<string name="send_prepare_hint">Prepare</string>
<string name="send_send_hint">Get rid of my Monero!</string>
<string name="send_preparing_progress">Preparing transaction</string>
<string name="send_amount_label">Amount</string>
<string name="send_fee_label">Fee</string>
<string name="send_dust_label">Dust</string>
<string name="big_amount">999999.999999999999</string>
<string-array name="mixin">