From 40da44222e75b5f50e0aa7992cdbeaea1b83ddea Mon Sep 17 00:00:00 2001
From: m2049r <30435443+m2049r@users.noreply.github.com>
Date: Sat, 19 Aug 2017 13:27:26 +0200
Subject: [PATCH] sending done + added Transfers + tweaks

---
 app/src/main/cpp/monerujo.cpp                 | 122 +++++++++---------
 .../com/m2049r/xmrwallet/LoginActivity.java   |   5 +-
 .../com/m2049r/xmrwallet/SendFragment.java    |  16 +--
 .../com/m2049r/xmrwallet/WalletActivity.java  |  51 +++++---
 .../com/m2049r/xmrwallet/WalletFragment.java  |  44 +++++--
 .../layout/TransactionInfoAdapter.java        |  12 +-
 .../xmrwallet/model/PendingTransaction.java   |   2 +-
 .../xmrwallet/model/TransactionHistory.java   |  20 ++-
 .../xmrwallet/model/TransactionInfo.java      | 102 ++++++---------
 .../com/m2049r/xmrwallet/model/Transfer.java  |  31 +++++
 .../com/m2049r/xmrwallet/model/Wallet.java    |   4 +-
 .../service/MoneroHandlerThread.java          |   2 +-
 .../xmrwallet/service/WalletService.java      |  24 ++--
 .../com/m2049r/xmrwallet/util/Helper.java     |   4 +-
 app/src/main/res/layout/send_fragment.xml     |   5 +-
 app/src/main/res/layout/wallet_fragment.xml   |   1 +
 app/src/main/res/values/strings.xml           |   3 +-
 17 files changed, 256 insertions(+), 192 deletions(-)
 create mode 100644 app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java

diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp
index 57176b76..79c89df6 100644
--- a/app/src/main/cpp/monerujo.cpp
+++ b/app/src/main/cpp/monerujo.cpp
@@ -37,7 +37,7 @@ static JavaVM *cachedJVM;
 static jclass class_ArrayList;
 static jclass class_WalletListener;
 static jclass class_TransactionInfo;
-static jclass class_TransactionInfo$Transfer;
+static jclass class_Transfer;
 
 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
     cachedJVM = jvm;
@@ -52,8 +52,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
             jenv->FindClass("java/util/ArrayList")));
     class_TransactionInfo = static_cast<jclass>(jenv->NewGlobalRef(
             jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo")));
-    class_TransactionInfo$Transfer = static_cast<jclass>(jenv->NewGlobalRef(
-            jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo$Transfer")));
+    class_Transfer = static_cast<jclass>(jenv->NewGlobalRef(
+            jenv->FindClass("com/m2049r/xmrwallet/model/Transfer")));
     class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef(
             jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
     return JNI_VERSION_1_6;
@@ -174,7 +174,6 @@ struct MyWalletListener : Bitmonero::WalletListener {
         detachJVM(jenv, envStat);
     }
 
-
 /**
  * @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",
                                                               "()V");
         jenv->CallVoidMethod(jlistener, listenerClass_refreshed);
-
         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);
     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::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);
 }
 
+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) {
-    jmethodID c = env->GetMethodID(class_TransactionInfo, "<init>", "(J)V");
-    return env->NewObject(class_TransactionInfo, c, reinterpret_cast<jlong>(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());
+    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>
@@ -896,7 +940,7 @@ jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector)
     jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
                                                          "(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());
     for (Bitmonero::TransactionInfo *s: vector) {
         jobject info = newTransactionInfo(env, s);
@@ -907,31 +951,15 @@ jobject cpp2java(JNIEnv *env, std::vector<Bitmonero::TransactionInfo *> vector)
 }
 
 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);
+    LOGD("history->refresh()");
+    history->refresh();
+    LOGD("history->refresh() done");
     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,
-                                                                                      instance);
-    history->refresh();
-}
-
-/* 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
 JNIEXPORT jint JNICALL
 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);
     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);
@@ -993,36 +1021,6 @@ Java_com_m2049r_xmrwallet_model_TransactionInfo_getPaymentId(JNIEnv *env, jobjec
     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
 Java_com_m2049r_xmrwallet_model_TransactionInfo_getTransferCount(JNIEnv *env, jobject instance) {
     Bitmonero::TransactionInfo *info = getHandle<Bitmonero::TransactionInfo>(env, instance);
diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java
index 6448b0cc..99caab64 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java
@@ -53,6 +53,7 @@ import java.nio.channels.FileChannel;
 public class LoginActivity extends AppCompatActivity
         implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener {
     static final String TAG = "LoginActivity";
+    private static final String GENERATE_STACK = "gen";
 
     static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
 
@@ -264,7 +265,7 @@ public class LoginActivity extends AppCompatActivity
     }
 
     void startGenerateFragment() {
-        replaceFragment(new GenerateFragment(), "gen", null);
+        replaceFragment(new GenerateFragment(), GENERATE_STACK, null);
         Log.d(TAG, "GenerateFragment placed");
     }
 
@@ -397,7 +398,7 @@ public class LoginActivity extends AppCompatActivity
                 &&
                 (testWallet(walletPath, password) == Wallet.Status.Status_Ok);
         if (rc) {
-            popFragmentStack("gen");
+            popFragmentStack(GENERATE_STACK);
             Toast.makeText(LoginActivity.this,
                     getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show();
         } else {
diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
index 422040e3..22464820 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
@@ -41,8 +41,6 @@ import com.m2049r.xmrwallet.util.TxData;
 public class SendFragment extends Fragment {
     static final String TAG = "GenerateFragment";
 
-    static final public String ARG_WALLETID = "walletId";
-
     EditText etAddress;
     EditText etPaymentId;
     EditText etAmount;
@@ -56,11 +54,11 @@ public class SendFragment extends Fragment {
     TextView tvTxDust;
     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[] =
             {PendingTransaction.Priority.Priority_Low,
                     PendingTransaction.Priority.Priority_Medium,
-                    PendingTransaction.Priority.Priority_High}; // must macth the layout
+                    PendingTransaction.Priority.Priority_High}; // must match the layout XML
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -87,12 +85,12 @@ public class SendFragment extends Fragment {
 
         etAddress.setText("9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8");
         boolean testnet = WalletManager.getInstance().isTestNet();
-        // TODO die if NOT testnet
+        if (!testnet) throw new IllegalStateException("Sending TX only on testnet. sorry.");
+
         Helper.showKeyboard(getActivity());
         etAddress.requestFocus();
         etAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
             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 (addressOk()) {
                         etPaymentId.requestFocus();
@@ -180,7 +178,7 @@ public class SendFragment extends Fragment {
         int mixin = Mixins[sMixin.getSelectedItemPosition()];
         int priorityIndex = sPriority.getSelectedItemPosition();
         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(
                 dst_addr,
                 paymentId,
@@ -198,6 +196,7 @@ public class SendFragment extends Fragment {
 
         activityCallback.onPrepareSend(txData);
     }
+
     private void send() {
         activityCallback.onSend();
     }
@@ -227,11 +226,12 @@ public class SendFragment extends Fragment {
         if (status != PendingTransaction.Status.Status_Ok) {
             Log.d(TAG, "Wallet store failed: " + pendingTransaction.getErrorString());
         }
+        /*
         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()));
diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
index 9e6266ba..e140abab 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
@@ -47,6 +47,10 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
 
     private boolean synced = false;
 
+    public boolean isSynced() {
+        return synced;
+    }
+
     @Override
     protected void onStart() {
         super.onStart();
@@ -187,8 +191,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
             wl.acquire();
             Log.d(TAG, "WakeLock acquired");
         } catch (SecurityException ex) {
-            Log.d(TAG, "WakeLock NOT acquired");
-            Log.d(TAG, ex.getLocalizedMessage());
+            Log.d(TAG, "WakeLock NOT acquired: " + ex.getLocalizedMessage());
             wl = null;
         }
     }
@@ -252,25 +255,39 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
     ///////////////////////////
     // WalletService.Observer
     ///////////////////////////
+
+    // refresh and return if successful
     @Override
-    public void onRefreshed(final Wallet wallet, final boolean full) {
+    public boolean onRefreshed(final Wallet wallet, final boolean full) {
         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
-        final WalletFragment walletFragment = (WalletFragment)
-                getFragmentManager().findFragmentById(R.id.fragment_container);
-        runOnUiThread(new Runnable() {
-            public void run() {
-                walletFragment.onRefreshed(wallet, full);
+        try {
+            final WalletFragment walletFragment = (WalletFragment)
+                    getFragmentManager().findFragmentById(R.id.fragment_container);
+            if (wallet.isSynchronized()) {
+                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
diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java
index e116c747..c52bbff8 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java
@@ -36,6 +36,7 @@ import android.widget.TextView;
 
 import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
 import com.m2049r.xmrwallet.model.TransactionInfo;
+import com.m2049r.xmrwallet.model.Transfer;
 import com.m2049r.xmrwallet.model.Wallet;
 
 import java.text.NumberFormat;
@@ -53,6 +54,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
     LinearLayout llProgress;
     TextView tvProgress;
     ProgressBar pbProgress;
+    Button bSend;
 
     @Override
     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);
         tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress);
         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.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();
 
         return view;
     }
 
+
     // Callbacks from TransactionInfoAdapter
     @Override
     public void onInteraction(final View view, final TransactionInfo infoItem) {
@@ -103,7 +111,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 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);
             }
         });
@@ -113,11 +121,25 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
             public void onClick(DialogInterface dialog, int which) {
             }
         });
-        builder.setMessage("TX ID: " + infoItem.getHash() +
-                "\nPayment ID: " + infoItem.getPaymentId() +
-                "\nBlockHeight: " + infoItem.getBlockHeight() +
-                "\nAmount: " + Wallet.getDisplayAmount(infoItem.getAmount()) +
-                "\nFee: " + Wallet.getDisplayAmount(infoItem.getFee()));
+        // TODO use strings.xml
+        StringBuffer sb = new StringBuffer();
+        sb.append("TX ID: ").append(infoItem.hash);
+        sb.append("\nPayment ID: ").append(infoItem.paymentId);
+        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();
         alert1.show();
     }
@@ -133,6 +155,11 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
         updateStatus(wallet);
     }
 
+    public void onSynced() { // TODO watchonly
+        bSend.setVisibility(View.VISIBLE);
+        bSend.setEnabled(true);
+    }
+
     public void onProgress(final String text) {
         if (text != null) {
             tvProgress.setText(text);
@@ -225,6 +252,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
 
         void onSendRequest();
 
+        boolean isSynced();
     }
 
     @Override
diff --git a/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java
index 88ab78e4..57ef2f08 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java
@@ -88,8 +88,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
             Collections.sort(data, new Comparator<TransactionInfo>() {
                 @Override
                 public int compare(TransactionInfo o1, TransactionInfo o2) {
-                    long b1 = o1.getBlockHeight();
-                    long b2 = o2.getBlockHeight();
+                    long b1 = o1.blockheight;
+                    long b2 = o2.blockheight;
                     return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
                 }
             });
@@ -134,20 +134,20 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
 
         void bind(int position) {
             this.infoItem = infoItems.get(position);
-            String displayAmount = Wallet.getDisplayAmount(infoItem.getAmount());
+            String displayAmount = Wallet.getDisplayAmount(infoItem.amount);
             // TODO fix this with i8n code
             String amountParts[] = displayAmount.split("\\.");
             // TODO what if there is no decimal point?
 
             this.tvAmount.setText(amountParts[0]);
             this.tvAmountDecimal.setText(amountParts[1]);
-            if (infoItem.getDirection() == TransactionInfo.Direction.Direction_In) {
+            if (infoItem.direction == TransactionInfo.Direction.Direction_In) {
                 setTxColour(TX_GREEN);
             } else {
                 setTxColour(TX_RED);
             }
-            this.tvDate.setText(getDate(infoItem.getTimestamp()));
-            this.tvTime.setText(getTime(infoItem.getTimestamp()));
+            this.tvDate.setText(getDate(infoItem.timestamp));
+            this.tvTime.setText(getTime(infoItem.timestamp));
 
             itemView.setOnClickListener(this);
         }
diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/PendingTransaction.java b/app/src/main/java/com/m2049r/xmrwallet/model/PendingTransaction.java
index d9256d13..18ce09b4 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/model/PendingTransaction.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/model/PendingTransaction.java
@@ -37,7 +37,7 @@ public class PendingTransaction {
         Priority_Low(1),
         Priority_Medium(2),
         Priority_High(3),
-        Priority_Last(4); // TODO is this true?
+        Priority_Last(4);
 
         private int value;
 
diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java
index 58a3c5a5..824ea18e 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionHistory.java
@@ -16,6 +16,7 @@
 
 package com.m2049r.xmrwallet.model;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class TransactionHistory {
@@ -29,6 +30,7 @@ public class TransactionHistory {
         this.handle = handle;
     }
 
+    /*
     public TransactionInfo getTransaction(int i) {
         long infoHandle = getTransactionByIndexJ(i);
         return new TransactionInfo(infoHandle);
@@ -38,7 +40,7 @@ public class TransactionHistory {
         long infoHandle = getTransactionByIdJ(id);
         return new TransactionInfo(infoHandle);
     }
-
+    */
     /*
         public List<TransactionInfo> getAll() {
             List<Long> handles = getAllJ();
@@ -51,12 +53,20 @@ public class TransactionHistory {
     */
     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;
+    }
 
-    public native void refresh();
+    private List<TransactionInfo> transactions = new ArrayList<>();
+
+    public void refresh() {
+        transactions = refreshJ();
+    }
+
+    private native List<TransactionInfo> refreshJ();
 
 }
diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java
index a32444d4..fe986b69 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java
@@ -16,79 +16,57 @@
 
 package com.m2049r.xmrwallet.model;
 
+import java.util.List;
+
+// this is not the TransactionInfo from the API as that is owned by the TransactionHistory
+// this is a POJO for the TransactionInfoAdapter
 public class TransactionInfo {
-    static {
-        System.loadLibrary("monerujo");
-    }
-
-    public long handle;
-
-    TransactionInfo(long handle) {
-        this.handle = handle;
-    }
+    static final String TAG = "TransactionInfo";
 
     public enum Direction {
         Direction_In,
         Direction_Out
     }
 
-    public class Transfer {
-        long amount;
-        String address;
+    public Direction direction;
+    public boolean isPending;
+    public boolean isFailed;
+    public long amount;
+    public long fee;
+    public long blockheight;
+    public String hash;
+    public long timestamp;
+    public String paymentId;
+    public long confirmations;
+    public List<Transfer> transfers;
 
-        public Transfer(long amount, String address) {
-            this.amount = amount;
-            this.address = address;
-        }
-
-        public long getAmount() {
-            return amount;
-        }
-
-        public String getAddress() {
-            return address;
-        }
+    public TransactionInfo(
+            int direction,
+            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() {
-        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();
-*/
-
 }
diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java b/app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java
new file mode 100644
index 00000000..988fe168
--- /dev/null
+++ b/app/src/main/java/com/m2049r/xmrwallet/model/Transfer.java
@@ -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;
+    }
+}
diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java
index b7cf0755..6c1a0a77 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java
@@ -162,7 +162,6 @@ public class Wallet {
 //TODO virtual int autoRefreshInterval() const = 0;
 
 
-    // TODO - good place to keep this ?
     private PendingTransaction pendingTransaction = null;
 
     public PendingTransaction getPendingTransaction() {
@@ -205,8 +204,11 @@ 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;
     }
diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java b/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java
index a214ded8..74b66ca2 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java
@@ -26,7 +26,7 @@ import android.os.Process;
 /**
  * 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.
- * 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 {
     // from src/cryptonote_config.h
diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
index e4544711..d913ab6d 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
@@ -113,10 +113,12 @@ public class WalletService extends Service {
                     updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
                     if (!wallet.isSynchronized()) {
                         // we want to see our transactions as they come in
+                        Log.d(TAG, "newBlock() refresh history");
                         wallet.getHistory().refresh();
+                        Log.d(TAG, "newBlock() history refreshed");
                         int txCount = wallet.getHistory().getCount();
                         if (txCount > lastTxCount) {
-                            lastTxCount = txCount;
+                            lastTxCount = txCount; // TODO maybe do this later
                             fullRefresh = true;
                         }
                     }
@@ -138,10 +140,10 @@ public class WalletService extends Service {
             if (updated) {
                 if (observer != null) {
                     updateDaemonState(wallet, 0);
-                    TransactionHistory history = wallet.getHistory();
-                    history.refresh();
-                    if (observer != null) observer.onRefreshed(wallet, true);
-                    updated = false;
+                    wallet.getHistory().refresh();
+                    if (observer != null) {
+                        updated = !observer.onRefreshed(wallet, true);
+                    }
                 }
             }
         }
@@ -196,7 +198,7 @@ public class WalletService extends Service {
     }
 
     public interface Observer {
-        void onRefreshed(Wallet wallet, boolean full);
+        boolean onRefreshed(Wallet wallet, boolean full);
 
         void onProgress(String text);
 
@@ -262,7 +264,7 @@ public class WalletService extends Service {
                     if (cmd.equals(REQUEST_CMD_LOAD)) {
                         String walletId = extras.getString(REQUEST_WALLET, 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) {
                             showProgress(getString(R.string.status_wallet_loading));
                             showProgress(10);
@@ -270,7 +272,7 @@ public class WalletService extends Service {
                         }
                     } else if (cmd.equals(REQUEST_CMD_STORE)) {
                         Wallet myWallet = getWallet();
-                        Log.d(TAG, "storing wallet: " + myWallet.getName());
+                        Log.d(TAG, "STORE wallet: " + myWallet.getName());
                         boolean rc = myWallet.store();
                         Log.d(TAG, "wallet stored: " + myWallet.getName() + " with rc=" + rc);
                         if (!rc) {
@@ -279,14 +281,12 @@ public class WalletService extends Service {
                         if (observer != null) observer.onWalletStored(rc);
                     } else if (cmd.equals(REQUEST_CMD_TX)) {
                         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);
                         PendingTransaction pendingTransaction = myWallet.createTransaction(
                                 txData.dst_addr, txData.paymentId, txData.amount, txData.mixin, txData.priority);
                         PendingTransaction.Status status = pendingTransaction.getStatus();
                         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) {
                             Log.d(TAG, "Create Transaction failed: " + pendingTransaction.getErrorString());
                         }
@@ -294,7 +294,7 @@ public class WalletService extends Service {
                         if (observer != null) observer.onCreatedTransaction(pendingTransaction);
                     } else if (cmd.equals(REQUEST_CMD_SEND)) {
                         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();
                         if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
                             Log.e(TAG, "PendingTransaction is " + pendingTransaction.getStatus());
diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
index 182daf57..f3454527 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
@@ -46,7 +46,7 @@ public class Helper {
             dir.mkdirs(); // try to make it
         }
         if (!dir.isDirectory()) {
-            String msg = "Directory " + dir.getAbsolutePath() + " does not exists.";
+            String msg = "Directory " + dir.getAbsolutePath() + " does not exist.";
             Log.e(TAG, 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 (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                     == 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};
                 context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
                 return false;
diff --git a/app/src/main/res/layout/send_fragment.xml b/app/src/main/res/layout/send_fragment.xml
index fcd2d0e7..2a2c4f60 100644
--- a/app/src/main/res/layout/send_fragment.xml
+++ b/app/src/main/res/layout/send_fragment.xml
@@ -104,7 +104,7 @@
         android:layout_marginTop="8dp"
         android:background="@color/colorPrimary"
         android:enabled="false"
-        android:text="@string/send_send_hint" />
+        android:text="@string/send_prepare_hint" />
 
     <LinearLayout
         android:id="@+id/llConfirmSend"
@@ -136,7 +136,6 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="2"
                 android:layout_marginLeft="8dp"
-                android:text="@string/big_amount"
                 android:textAlignment="textEnd"
                 android:textSize="20sp" />
         </LinearLayout>
@@ -164,7 +163,6 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="2"
                 android:layout_marginLeft="8dp"
-                android:text="@string/big_amount"
                 android:textAlignment="textEnd"
                 android:textSize="20sp" />
         </LinearLayout>
@@ -192,7 +190,6 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="2"
                 android:layout_marginLeft="8dp"
-                android:text="@string/big_amount"
                 android:textAlignment="textEnd"
                 android:textSize="20sp" />
         </LinearLayout>
diff --git a/app/src/main/res/layout/wallet_fragment.xml b/app/src/main/res/layout/wallet_fragment.xml
index 01c5f055..6f24ed2d 100644
--- a/app/src/main/res/layout/wallet_fragment.xml
+++ b/app/src/main/res/layout/wallet_fragment.xml
@@ -128,6 +128,7 @@
             android:background="@color/colorPrimary"
             android:enabled="true"
             android:text="@string/wallet_send_hint"
+            android:visibility="gone"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintRight_toRightOf="parent" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c432e7da..9a70556e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -100,7 +100,8 @@
     <string name="send_mixin_hint">Mixin</string>
     <string name="send_sweep_hint">Sweep</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_fee_label">Fee</string>