wownero
/
wownerujo
Archived
4
0
Fork 0

sending done + added Transfers + tweaks

upstream
m2049r 7 years ago
parent b0efdca928
commit 40da44222e

@ -37,7 +37,7 @@ static JavaVM *cachedJVM;
static jclass class_ArrayList; static jclass class_ArrayList;
static jclass class_WalletListener; static jclass class_WalletListener;
static jclass class_TransactionInfo; static jclass class_TransactionInfo;
static jclass class_TransactionInfo$Transfer; static jclass class_Transfer;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm; cachedJVM = jvm;
@ -52,8 +52,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
jenv->FindClass("java/util/ArrayList"))); jenv->FindClass("java/util/ArrayList")));
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_TransactionInfo$Transfer = static_cast<jclass>(jenv->NewGlobalRef( class_Transfer = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo$Transfer"))); jenv->FindClass("com/m2049r/xmrwallet/model/Transfer")));
class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef( class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener"))); jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
return JNI_VERSION_1_6; return JNI_VERSION_1_6;
@ -174,7 +174,6 @@ struct MyWalletListener : Bitmonero::WalletListener {
detachJVM(jenv, envStat); detachJVM(jenv, envStat);
} }
/** /**
* @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously
*/ */
@ -189,7 +188,6 @@ struct MyWalletListener : Bitmonero::WalletListener {
jmethodID listenerClass_refreshed = jenv->GetMethodID(class_WalletListener, "refreshed", jmethodID listenerClass_refreshed = jenv->GetMethodID(class_WalletListener, "refreshed",
"()V"); "()V");
jenv->CallVoidMethod(jlistener, listenerClass_refreshed); jenv->CallVoidMethod(jlistener, listenerClass_refreshed);
detachJVM(jenv, envStat); detachJVM(jenv, envStat);
} }
}; };
@ -767,9 +765,6 @@ Java_com_m2049r_xmrwallet_model_Wallet_createTransactionJ(JNIEnv *env, jobject i
const char *_payment_id = env->GetStringUTFChars(payment_id, JNI_FALSE); const char *_payment_id = env->GetStringUTFChars(payment_id, JNI_FALSE);
Bitmonero::PendingTransaction::Priority _priority = Bitmonero::PendingTransaction::Priority _priority =
static_cast<Bitmonero::PendingTransaction::Priority>(priority); static_cast<Bitmonero::PendingTransaction::Priority>(priority);
LOGD("Priority_Last is %i", static_cast<int>(Bitmonero::PendingTransaction::Priority_Last));
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
Bitmonero::PendingTransaction *tx = wallet->createTransaction(_dst_addr, _payment_id, Bitmonero::PendingTransaction *tx = wallet->createTransaction(_dst_addr, _payment_id,
@ -883,9 +878,58 @@ Java_com_m2049r_xmrwallet_model_TransactionHistory_getTransactionByIdJ(JNIEnv *e
return reinterpret_cast<jlong>(info); 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");
jstring _address = env->NewStringUTF(address.c_str());
jobject transfer = env->NewObject(class_Transfer, c, amount, _address);
env->DeleteLocalRef(_address);
return transfer;
}
jobject newTransferList(JNIEnv *env, Bitmonero::TransactionInfo *info) {
const std::vector<Bitmonero::TransactionInfo::Transfer> &transfers = info->transfers();
// make new ArrayList
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 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);
env->DeleteLocalRef(element);
}
return result;
}
jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>", "(J)V"); jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>",
return env->NewObject(class_TransactionInfo, c, reinterpret_cast<jlong>(info)); "(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());
jobject result = env->NewObject(class_TransactionInfo, c,
info->direction(),
info->isPending(),
info->isFailed(),
info->amount(),
info->fee(),
info->blockHeight(),
_hash,
static_cast<jlong> (info->timestamp()),
_paymentId,
info->confirmations(),
transfers);
env->DeleteLocalRef(transfers);
env->DeleteLocalRef(_hash);
env->DeleteLocalRef(_paymentId);
LOGD("newTransactionInfo X");
return result;
} }
#include <stdio.h> #include <stdio.h>
@ -896,7 +940,7 @@ jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector)
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
"(Ljava/lang/Object;)Z"); "(Ljava/lang/Object;)Z");
//LOGD(std::to_string(vector.size()).c_str()); LOGD("%s", std::to_string(vector.size()).c_str());
jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, vector.size()); jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, vector.size());
for (Bitmonero::TransactionInfo *s: vector) { for (Bitmonero::TransactionInfo *s: vector) {
jobject info = newTransactionInfo(env, s); jobject info = newTransactionInfo(env, s);
@ -907,31 +951,15 @@ jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector)
} }
JNIEXPORT jobject JNICALL JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_getAll(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_TransactionHistory_refreshJ(JNIEnv *env, jobject instance) {
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance);
return cpp2java(env, history->getAll());
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_refresh(JNIEnv *env, jobject instance) {
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env, Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance); instance);
LOGD("history->refresh()");
history->refresh(); history->refresh();
LOGD("history->refresh() done");
return cpp2java(env, history->getAll());
} }
/* this is wrong - history object belongs to wallet
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_TransactionHistory_dispose(JNIEnv *env, jobject instance) {
Bitmonero::TransactionHistory *history = getHandle<Bitmonero::TransactionHistory>(env,
instance);
if (history != nullptr) {
setHandle<long>(env, instance, 0);
delete history;
}
}
*/
// TransactionInfo // TransactionInfo
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getDirectionJ(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_TransactionInfo_getDirectionJ(JNIEnv *env, jobject instance) {
@ -974,7 +1002,7 @@ Java_com_m2049r_xmrwallet_model_TransactionInfo_getConfirmations(JNIEnv *env, jo
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance); Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
return info->confirmations(); return info->confirmations();
} }
//TODO remove all these
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getHash(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_TransactionInfo_getHash(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance); Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
@ -993,36 +1021,6 @@ Java_com_m2049r_xmrwallet_model_TransactionInfo_getPaymentId(JNIEnv *env, jobjec
return env->NewStringUTF(info->paymentId().c_str()); return env->NewStringUTF(info->paymentId().c_str());
} }
jobject newTransferInstance(JNIEnv *env, jobject transactionInfo, long amount,
const std::string &address) {
jmethodID methodID = env->GetMethodID(class_TransactionInfo$Transfer, "<init>",
"(JL/java.lang/String;)V");
jstring _address = env->NewStringUTF(address.c_str());
jobject transfer = env->NewObject(class_TransactionInfo$Transfer, methodID, amount, _address);
env->DeleteLocalRef(_address);
return transfer;
}
JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransfersJ(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
const std::vector<Bitmonero::TransactionInfo::Transfer> &transfers = info->transfers();
// make new ArrayList
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 result = env->NewObject(class_ArrayList, java_util_ArrayList_, transfers.size());
// create Transfer objects and stick them in the List
for (const Bitmonero::TransactionInfo::Transfer &s: transfers) {
jobject element = newTransferInstance(env, instance, s.amount, s.address);
env->CallBooleanMethod(result, java_util_ArrayList_add, element);
env->DeleteLocalRef(element);
}
return result;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransferCount(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransferCount(JNIEnv *env, jobject instance) {
Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance); Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);

@ -53,6 +53,7 @@ import java.nio.channels.FileChannel;
public class LoginActivity extends AppCompatActivity public class LoginActivity extends AppCompatActivity
implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener {
static final String TAG = "LoginActivity"; static final String TAG = "LoginActivity";
private static final String GENERATE_STACK = "gen";
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
@ -264,7 +265,7 @@ public class LoginActivity extends AppCompatActivity
} }
void startGenerateFragment() { void startGenerateFragment() {
replaceFragment(new GenerateFragment(), "gen", null); replaceFragment(new GenerateFragment(), GENERATE_STACK, null);
Log.d(TAG, "GenerateFragment placed"); Log.d(TAG, "GenerateFragment placed");
} }
@ -397,7 +398,7 @@ public class LoginActivity extends AppCompatActivity
&& &&
(testWallet(walletPath, password) == Wallet.Status.Status_Ok); (testWallet(walletPath, password) == Wallet.Status.Status_Ok);
if (rc) { if (rc) {
popFragmentStack("gen"); popFragmentStack(GENERATE_STACK);
Toast.makeText(LoginActivity.this, Toast.makeText(LoginActivity.this,
getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show();
} else { } else {

@ -41,8 +41,6 @@ import com.m2049r.xmrwallet.util.TxData;
public class SendFragment extends Fragment { public class SendFragment extends Fragment {
static final String TAG = "GenerateFragment"; static final String TAG = "GenerateFragment";
static final public String ARG_WALLETID = "walletId";
EditText etAddress; EditText etAddress;
EditText etPaymentId; EditText etPaymentId;
EditText etAmount; EditText etAmount;
@ -56,11 +54,11 @@ public class SendFragment extends Fragment {
TextView tvTxDust; TextView tvTxDust;
Button bSend; Button bSend;
final static int Mixins[] = {4, 6, 8, 10, 13}; // must macth the layout final static int Mixins[] = {4, 6, 8, 10, 13}; // must match the layout XML
final static PendingTransaction.Priority Priorities[] = final static PendingTransaction.Priority Priorities[] =
{PendingTransaction.Priority.Priority_Low, {PendingTransaction.Priority.Priority_Low,
PendingTransaction.Priority.Priority_Medium, PendingTransaction.Priority.Priority_Medium,
PendingTransaction.Priority.Priority_High}; // must macth the layout PendingTransaction.Priority.Priority_High}; // must match the layout XML
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -87,12 +85,12 @@ public class SendFragment extends Fragment {
etAddress.setText("9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8"); etAddress.setText("9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8");
boolean testnet = WalletManager.getInstance().isTestNet(); boolean testnet = WalletManager.getInstance().isTestNet();
// TODO die if NOT testnet if (!testnet) throw new IllegalStateException("Sending TX only on testnet. sorry.");
Helper.showKeyboard(getActivity()); Helper.showKeyboard(getActivity());
etAddress.requestFocus(); etAddress.requestFocus();
etAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { etAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Log.d(TAG, actionId + "/" + (event == null ? null : event.toString()));
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_NEXT)) {
if (addressOk()) { if (addressOk()) {
etPaymentId.requestFocus(); etPaymentId.requestFocus();
@ -180,7 +178,7 @@ public class SendFragment extends Fragment {
int mixin = Mixins[sMixin.getSelectedItemPosition()]; int mixin = Mixins[sMixin.getSelectedItemPosition()];
int priorityIndex = sPriority.getSelectedItemPosition(); int priorityIndex = sPriority.getSelectedItemPosition();
PendingTransaction.Priority priority = Priorities[priorityIndex]; PendingTransaction.Priority priority = Priorities[priorityIndex];
Log.d(TAG, dst_addr + "/" + paymentId + "/" + amount + "/" + mixin + "/" + priority.toString()); //Log.d(TAG, dst_addr + "/" + paymentId + "/" + amount + "/" + mixin + "/" + priority.toString());
TxData txData = new TxData( TxData txData = new TxData(
dst_addr, dst_addr,
paymentId, paymentId,
@ -198,6 +196,7 @@ public class SendFragment extends Fragment {
activityCallback.onPrepareSend(txData); activityCallback.onPrepareSend(txData);
} }
private void send() { private void send() {
activityCallback.onSend(); activityCallback.onSend();
} }
@ -227,11 +226,12 @@ public class SendFragment extends Fragment {
if (status != PendingTransaction.Status.Status_Ok) { if (status != PendingTransaction.Status.Status_Ok) {
Log.d(TAG, "Wallet store failed: " + pendingTransaction.getErrorString()); Log.d(TAG, "Wallet store failed: " + pendingTransaction.getErrorString());
} }
/*
Log.d(TAG, "transaction amount " + pendingTransaction.getAmount()); Log.d(TAG, "transaction amount " + pendingTransaction.getAmount());
Log.d(TAG, "transaction fee " + pendingTransaction.getFee()); Log.d(TAG, "transaction fee " + pendingTransaction.getFee());
Log.d(TAG, "transaction dust " + pendingTransaction.getDust()); Log.d(TAG, "transaction dust " + pendingTransaction.getDust());
Log.d(TAG, "transactions " + pendingTransaction.getTxCount()); Log.d(TAG, "transactions " + pendingTransaction.getTxCount());
*/
llConfirmSend.setVisibility(View.VISIBLE); llConfirmSend.setVisibility(View.VISIBLE);
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount())); tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount()));
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee())); tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));

@ -47,6 +47,10 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
private boolean synced = false; private boolean synced = false;
public boolean isSynced() {
return synced;
}
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
@ -187,8 +191,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
wl.acquire(); wl.acquire();
Log.d(TAG, "WakeLock acquired"); Log.d(TAG, "WakeLock acquired");
} catch (SecurityException ex) { } catch (SecurityException ex) {
Log.d(TAG, "WakeLock NOT acquired"); Log.d(TAG, "WakeLock NOT acquired: " + ex.getLocalizedMessage());
Log.d(TAG, ex.getLocalizedMessage());
wl = null; wl = null;
} }
} }
@ -252,25 +255,39 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
/////////////////////////// ///////////////////////////
// WalletService.Observer // WalletService.Observer
/////////////////////////// ///////////////////////////
// refresh and return if successful
@Override @Override
public void onRefreshed(final Wallet wallet, final boolean full) { public boolean onRefreshed(final Wallet wallet, final boolean full) {
Log.d(TAG, "onRefreshed()"); Log.d(TAG, "onRefreshed()");
if (wallet.isSynchronized()) {
releaseWakeLock(); // the idea is to stay awake until synced
if (!synced) {
onProgress(null);
saveWallet(); // save on first sync
synced = true;
}
}
// TODO check which fragment is loaded // TODO check which fragment is loaded
final WalletFragment walletFragment = (WalletFragment) try {
getFragmentManager().findFragmentById(R.id.fragment_container); final WalletFragment walletFragment = (WalletFragment)
runOnUiThread(new Runnable() { getFragmentManager().findFragmentById(R.id.fragment_container);
public void run() { if (wallet.isSynchronized()) {
walletFragment.onRefreshed(wallet, full); releaseWakeLock(); // the idea is to stay awake until synced
if (!synced) {
onProgress(null);
saveWallet(); // save on first sync
synced = true;
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onSynced();
}
});
}
} }
}); runOnUiThread(new Runnable() {
public void run() {
walletFragment.onRefreshed(wallet, full);
}
});
return true;
} catch (ClassCastException ex) {
// not in wallet fragment (probably send monero)
// keep calm and carry on
}
return false;
} }
@Override @Override

@ -36,6 +36,7 @@ import android.widget.TextView;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import java.text.NumberFormat; import java.text.NumberFormat;
@ -53,6 +54,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
LinearLayout llProgress; LinearLayout llProgress;
TextView tvProgress; TextView tvProgress;
ProgressBar pbProgress; ProgressBar pbProgress;
Button bSend;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -66,7 +68,8 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
tvUnlockedBalance = (TextView) view.findViewById(R.id.tvUnlockedBalance); tvUnlockedBalance = (TextView) view.findViewById(R.id.tvUnlockedBalance);
tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress); tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress);
tvConnectionStatus = (TextView) view.findViewById(R.id.tvConnectionStatus); tvConnectionStatus = (TextView) view.findViewById(R.id.tvConnectionStatus);
Button bSend = (Button) view.findViewById(R.id.bSend);
bSend = (Button) view.findViewById(R.id.bSend);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list); RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration itemDecoration = new
@ -85,13 +88,18 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
} }
}); });
activityCallback.setTitle(getString(R.string.status_wallet_loading)); if (activityCallback.isSynced()) {
onSynced();
}
// activityCallback.setTitle(getString(R.string.status_wallet_loading));
activityCallback.forceUpdate(); activityCallback.forceUpdate();
return view; return view;
} }
// Callbacks from TransactionInfoAdapter // Callbacks from TransactionInfoAdapter
@Override @Override
public void onInteraction(final View view, final TransactionInfo infoItem) { public void onInteraction(final View view, final TransactionInfo infoItem) {
@ -103,7 +111,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
ClipboardManager clipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); ClipboardManager clipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("TX", infoItem.getHash()); ClipData clip = ClipData.newPlainText("TX", infoItem.hash);
clipboardManager.setPrimaryClip(clip); clipboardManager.setPrimaryClip(clip);
} }
}); });
@ -113,11 +121,25 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
} }
}); });
builder.setMessage("TX ID: " + infoItem.getHash() + // TODO use strings.xml
"\nPayment ID: " + infoItem.getPaymentId() + StringBuffer sb = new StringBuffer();
"\nBlockHeight: " + infoItem.getBlockHeight() + sb.append("TX ID: ").append(infoItem.hash);
"\nAmount: " + Wallet.getDisplayAmount(infoItem.getAmount()) + sb.append("\nPayment ID: ").append(infoItem.paymentId);
"\nFee: " + Wallet.getDisplayAmount(infoItem.getFee())); sb.append("\nBlockHeight: ").append(infoItem.blockheight);
sb.append("\nAmount: ");
sb.append(infoItem.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
sb.append(Wallet.getDisplayAmount(infoItem.amount));
sb.append("\nFee: ").append(Wallet.getDisplayAmount(infoItem.fee));
sb.append("\nTransfers:");
if (infoItem.transfers.size() > 0) {
for (Transfer transfer : infoItem.transfers) {
sb.append("\n[").append(transfer.address.substring(0, 6)).append("] ");
sb.append(Wallet.getDisplayAmount(transfer.amount));
}
} else {
sb.append("-");
}
builder.setMessage(sb.toString());
AlertDialog alert1 = builder.create(); AlertDialog alert1 = builder.create();
alert1.show(); alert1.show();
} }
@ -133,6 +155,11 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
updateStatus(wallet); updateStatus(wallet);
} }
public void onSynced() { // TODO watchonly
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
public void onProgress(final String text) { public void onProgress(final String text) {
if (text != null) { if (text != null) {
tvProgress.setText(text); tvProgress.setText(text);
@ -225,6 +252,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
void onSendRequest(); void onSendRequest();
boolean isSynced();
} }
@Override @Override

@ -88,8 +88,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
Collections.sort(data, new Comparator<TransactionInfo>() { Collections.sort(data, new Comparator<TransactionInfo>() {
@Override @Override
public int compare(TransactionInfo o1, TransactionInfo o2) { public int compare(TransactionInfo o1, TransactionInfo o2) {
long b1 = o1.getBlockHeight(); long b1 = o1.blockheight;
long b2 = o2.getBlockHeight(); long b2 = o2.blockheight;
return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0; return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
} }
}); });
@ -134,20 +134,20 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
void bind(int position) { void bind(int position) {
this.infoItem = infoItems.get(position); this.infoItem = infoItems.get(position);
String displayAmount = Wallet.getDisplayAmount(infoItem.getAmount()); String displayAmount = Wallet.getDisplayAmount(infoItem.amount);
// TODO fix this with i8n code // TODO fix this with i8n code
String amountParts[] = displayAmount.split("\\."); String amountParts[] = displayAmount.split("\\.");
// TODO what if there is no decimal point? // TODO what if there is no decimal point?
this.tvAmount.setText(amountParts[0]); this.tvAmount.setText(amountParts[0]);
this.tvAmountDecimal.setText(amountParts[1]); this.tvAmountDecimal.setText(amountParts[1]);
if (infoItem.getDirection() == TransactionInfo.Direction.Direction_In) { if (infoItem.direction == TransactionInfo.Direction.Direction_In) {
setTxColour(TX_GREEN); setTxColour(TX_GREEN);
} else { } else {
setTxColour(TX_RED); setTxColour(TX_RED);
} }
this.tvDate.setText(getDate(infoItem.getTimestamp())); this.tvDate.setText(getDate(infoItem.timestamp));
this.tvTime.setText(getTime(infoItem.getTimestamp())); this.tvTime.setText(getTime(infoItem.timestamp));
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
} }

@ -37,7 +37,7 @@ public class PendingTransaction {
Priority_Low(1), Priority_Low(1),
Priority_Medium(2), Priority_Medium(2),
Priority_High(3), Priority_High(3),
Priority_Last(4); // TODO is this true? Priority_Last(4);
private int value; private int value;

@ -16,6 +16,7 @@
package com.m2049r.xmrwallet.model; package com.m2049r.xmrwallet.model;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class TransactionHistory { public class TransactionHistory {
@ -29,6 +30,7 @@ public class TransactionHistory {
this.handle = handle; this.handle = handle;
} }
/*
public TransactionInfo getTransaction(int i) { public TransactionInfo getTransaction(int i) {
long infoHandle = getTransactionByIndexJ(i); long infoHandle = getTransactionByIndexJ(i);
return new TransactionInfo(infoHandle); return new TransactionInfo(infoHandle);
@ -38,7 +40,7 @@ public class TransactionHistory {
long infoHandle = getTransactionByIdJ(id); long infoHandle = getTransactionByIdJ(id);
return new TransactionInfo(infoHandle); return new TransactionInfo(infoHandle);
} }
*/
/* /*
public List<TransactionInfo> getAll() { public List<TransactionInfo> getAll() {
List<Long> handles = getAllJ(); List<Long> handles = getAllJ();
@ -51,12 +53,20 @@ public class TransactionHistory {
*/ */
public native int getCount(); public native int getCount();
private native long getTransactionByIndexJ(int i); //private native long getTransactionByIndexJ(int i);
private native long getTransactionByIdJ(String id); //private native long getTransactionByIdJ(String id);
public native List<TransactionInfo> getAll(); public List<TransactionInfo> getAll() {
return transactions;
}
private List<TransactionInfo> transactions = new ArrayList<>();
public void refresh() {
transactions = refreshJ();
}
public native void refresh(); private native List<TransactionInfo> refreshJ();
} }

@ -16,79 +16,57 @@
package com.m2049r.xmrwallet.model; package com.m2049r.xmrwallet.model;
public class TransactionInfo { import java.util.List;
static {
System.loadLibrary("monerujo");
}
public long handle; // this is not the TransactionInfo from the API as that is owned by the TransactionHistory
// this is a POJO for the TransactionInfoAdapter
TransactionInfo(long handle) { public class TransactionInfo {
this.handle = handle; static final String TAG = "TransactionInfo";
}
public enum Direction { public enum Direction {
Direction_In, Direction_In,
Direction_Out Direction_Out
} }
public class Transfer { public Direction direction;
long amount; public boolean isPending;
String address; public boolean isFailed;
public long amount;
public Transfer(long amount, String address) { public long fee;
this.amount = amount; public long blockheight;
this.address = address; public String hash;
} public long timestamp;
public String paymentId;
public long getAmount() { public long confirmations;
return amount; public List<Transfer> transfers;
}
public TransactionInfo(
public String getAddress() { int direction,
return address; boolean isPending,
} boolean isFailed,
long amount,
long fee,
long blockheight,
String hash,
long timestamp,
String paymentId,
long confirmations,
List<Transfer> transfers) {
this.direction = Direction.values()[direction];
this.isPending = isPending;
this.isFailed = isFailed;
this.amount = amount;
this.fee = fee;
this.blockheight = blockheight;
this.hash = hash;
this.timestamp = timestamp;
this.paymentId = paymentId;
this.confirmations = confirmations;
this.transfers = transfers;
} }
public String toString() { public String toString() {
return getDirection() + "@" + getBlockHeight() + " " + getAmount(); return direction + "@" + blockheight + " " + amount;
}
public Direction getDirection() {
return TransactionInfo.Direction.values()[getDirectionJ()];
} }
public native int getDirectionJ();
public native boolean isPending();
public native boolean isFailed();
public native long getAmount();
public native long getFee();
public native long getBlockHeight();
public native long getConfirmations();
public native String getHash();
public native long getTimestamp();
public native String getPaymentId();
/*
private List<Transfer> transfers;
public List<Transfer> getTransfers() { // not threadsafe
if (this.transfers == null) {
this.transfers = getTransfersJ();
}
return this.transfers;
}
private native List<Transfer> getTransfersJ();
*/
} }

@ -0,0 +1,31 @@
/*
* Copyright (c) 2017 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 android.util.Log;
public class Transfer {
public long amount;
public String address;
public Transfer(long amount, String address) {
Log.d("Transfer", address + "/" + amount);
//Log.d("Transfer", "/" + amount);
this.amount = amount;
this.address = address;
}
}

@ -162,7 +162,6 @@ public class Wallet {
//TODO virtual int autoRefreshInterval() const = 0; //TODO virtual int autoRefreshInterval() const = 0;
// TODO - good place to keep this ?
private PendingTransaction pendingTransaction = null; private PendingTransaction pendingTransaction = null;
public PendingTransaction getPendingTransaction() { public PendingTransaction getPendingTransaction() {
@ -205,8 +204,11 @@ public class Wallet {
private TransactionHistory history = null; private TransactionHistory history = null;
public TransactionHistory getHistory() { public TransactionHistory getHistory() {
Log.d(TAG, "A");
if (history == null) { if (history == null) {
Log.d(TAG, "B");
history = new TransactionHistory(getHistoryJ()); history = new TransactionHistory(getHistoryJ());
Log.d(TAG, "C");
} }
return history; return history;
} }

@ -26,7 +26,7 @@ import android.os.Process;
/** /**
* Handy class for starting a new thread that has a looper. The looper can then be * Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called. * used to create handler classes. Note that start() must still be called.
* The started Thread has a stck size of STACK_SIZE (=3MB) * The started Thread has a stck size of STACK_SIZE (=5MB)
*/ */
public class MoneroHandlerThread extends Thread { public class MoneroHandlerThread extends Thread {
// from src/cryptonote_config.h // from src/cryptonote_config.h

@ -113,10 +113,12 @@ public class WalletService extends Service {
updateDaemonState(wallet, wallet.isSynchronized() ? height : 0); updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
if (!wallet.isSynchronized()) { if (!wallet.isSynchronized()) {
// we want to see our transactions as they come in // we want to see our transactions as they come in
Log.d(TAG, "newBlock() refresh history");
wallet.getHistory().refresh(); wallet.getHistory().refresh();
Log.d(TAG, "newBlock() history refreshed");
int txCount = wallet.getHistory().getCount(); int txCount = wallet.getHistory().getCount();
if (txCount > lastTxCount) { if (txCount > lastTxCount) {
lastTxCount = txCount; lastTxCount = txCount; // TODO maybe do this later
fullRefresh = true; fullRefresh = true;
} }
} }
@ -138,10 +140,10 @@ public class WalletService extends Service {
if (updated) { if (updated) {
if (observer != null) { if (observer != null) {
updateDaemonState(wallet, 0); updateDaemonState(wallet, 0);
TransactionHistory history = wallet.getHistory(); wallet.getHistory().refresh();
history.refresh(); if (observer != null) {
if (observer != null) observer.onRefreshed(wallet, true); updated = !observer.onRefreshed(wallet, true);
updated = false; }
} }
} }
} }
@ -196,7 +198,7 @@ public class WalletService extends Service {
} }
public interface Observer { public interface Observer {
void onRefreshed(Wallet wallet, boolean full); boolean onRefreshed(Wallet wallet, boolean full);
void onProgress(String text); void onProgress(String text);
@ -262,7 +264,7 @@ public class WalletService extends Service {
if (cmd.equals(REQUEST_CMD_LOAD)) { if (cmd.equals(REQUEST_CMD_LOAD)) {
String walletId = extras.getString(REQUEST_WALLET, null); String walletId = extras.getString(REQUEST_WALLET, null);
String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null); String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null);
Log.d(TAG, "LOAD wallet " + walletId);// + ":" + walletPw); Log.d(TAG, "LOAD wallet " + walletId);
if (walletId != null) { if (walletId != null) {
showProgress(getString(R.string.status_wallet_loading)); showProgress(getString(R.string.status_wallet_loading));
showProgress(10); showProgress(10);
@ -270,7 +272,7 @@ public class WalletService extends Service {
} }
} else if (cmd.equals(REQUEST_CMD_STORE)) { } else if (cmd.equals(REQUEST_CMD_STORE)) {
Wallet myWallet = getWallet(); Wallet myWallet = getWallet();
Log.d(TAG, "storing wallet: " + myWallet.getName()); Log.d(TAG, "STORE wallet: " + myWallet.getName());
boolean rc = myWallet.store(); boolean rc = myWallet.store();
Log.d(TAG, "wallet stored: " + myWallet.getName() + " with rc=" + rc); Log.d(TAG, "wallet stored: " + myWallet.getName() + " with rc=" + rc);
if (!rc) { if (!rc) {
@ -279,14 +281,12 @@ public class WalletService extends Service {
if (observer != null) observer.onWalletStored(rc); if (observer != null) observer.onWalletStored(rc);
} else if (cmd.equals(REQUEST_CMD_TX)) { } else if (cmd.equals(REQUEST_CMD_TX)) {
Wallet myWallet = getWallet(); Wallet myWallet = getWallet();
Log.d(TAG, "creating tx for wallet: " + myWallet.getName()); Log.d(TAG, "CREATE TX for wallet: " + myWallet.getName());
TxData txData = extras.getParcelable(REQUEST_CMD_TX_DATA); TxData txData = extras.getParcelable(REQUEST_CMD_TX_DATA);
PendingTransaction pendingTransaction = myWallet.createTransaction( PendingTransaction pendingTransaction = myWallet.createTransaction(
txData.dst_addr, txData.paymentId, txData.amount, txData.mixin, txData.priority); txData.dst_addr, txData.paymentId, txData.amount, txData.mixin, txData.priority);
PendingTransaction.Status status = pendingTransaction.getStatus(); PendingTransaction.Status status = pendingTransaction.getStatus();
Log.d(TAG, "transaction status " + status); Log.d(TAG, "transaction status " + status);
Log.d(TAG, "transaction amount " + pendingTransaction.getAmount());
Log.d(TAG, "transaction fee " + pendingTransaction.getFee());
if (status != PendingTransaction.Status.Status_Ok) { if (status != PendingTransaction.Status.Status_Ok) {
Log.d(TAG, "Create Transaction failed: " + pendingTransaction.getErrorString()); Log.d(TAG, "Create Transaction failed: " + pendingTransaction.getErrorString());
} }
@ -294,7 +294,7 @@ public class WalletService extends Service {
if (observer != null) observer.onCreatedTransaction(pendingTransaction); if (observer != null) observer.onCreatedTransaction(pendingTransaction);
} else if (cmd.equals(REQUEST_CMD_SEND)) { } else if (cmd.equals(REQUEST_CMD_SEND)) {
Wallet myWallet = getWallet(); Wallet myWallet = getWallet();
Log.d(TAG, "send tx for wallet: " + myWallet.getName()); Log.d(TAG, "SEND TX for wallet: " + myWallet.getName());
PendingTransaction pendingTransaction = myWallet.getPendingTransaction(); PendingTransaction pendingTransaction = myWallet.getPendingTransaction();
if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) { if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
Log.e(TAG, "PendingTransaction is " + pendingTransaction.getStatus()); Log.e(TAG, "PendingTransaction is " + pendingTransaction.getStatus());

@ -46,7 +46,7 @@ public class Helper {
dir.mkdirs(); // try to make it dir.mkdirs(); // try to make it
} }
if (!dir.isDirectory()) { if (!dir.isDirectory()) {
String msg = "Directory " + dir.getAbsolutePath() + " does not exists."; String msg = "Directory " + dir.getAbsolutePath() + " does not exist.";
Log.e(TAG, msg); Log.e(TAG, msg);
throw new IllegalStateException(msg); throw new IllegalStateException(msg);
} }
@ -59,7 +59,7 @@ public class Helper {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_DENIED) { == PackageManager.PERMISSION_DENIED) {
Log.d("permission", "permission denied to WRITE_EXTERNAL_STORAGE - requesting it"); Log.d(TAG, "Permission denied to WRITE_EXTERNAL_STORAGE - requesting it");
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
return false; return false;

@ -104,7 +104,7 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:enabled="false" android:enabled="false"
android:text="@string/send_send_hint" /> android:text="@string/send_prepare_hint" />
<LinearLayout <LinearLayout
android:id="@+id/llConfirmSend" android:id="@+id/llConfirmSend"
@ -136,7 +136,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="2"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:text="@string/big_amount"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>
@ -164,7 +163,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="2"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:text="@string/big_amount"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>
@ -192,7 +190,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="2"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:text="@string/big_amount"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>

@ -128,6 +128,7 @@
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:enabled="true" android:enabled="true"
android:text="@string/wallet_send_hint" android:text="@string/wallet_send_hint"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" /> app:layout_constraintRight_toRightOf="parent" />

@ -100,7 +100,8 @@
<string name="send_mixin_hint">Mixin</string> <string name="send_mixin_hint">Mixin</string>
<string name="send_sweep_hint">Sweep</string> <string name="send_sweep_hint">Sweep</string>
<string name="send_generate_paymentid_hint">Generate</string> <string name="send_generate_paymentid_hint">Generate</string>
<string name="send_send_hint">Get rid of my monero!</string> <string name="send_prepare_hint">Prepare</string>
<string name="send_send_hint">Get rid of my Monero!</string>
<string name="send_amount_label">Amount</string> <string name="send_amount_label">Amount</string>
<string name="send_fee_label">Fee</string> <string name="send_fee_label">Fee</string>