diff --git a/app/app.iml b/app/app.iml
index 25bc19b..b6bdf79 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -89,7 +89,6 @@
-
diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
index 336c827..9d92711 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
@@ -58,6 +58,10 @@ public class WalletActivity extends AppCompatActivity
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart()");
+ this.synced = false; // init syncing logic
+ }
+
+ private void startWalletService() {
acquireWakeLock();
Bundle extras = getIntent().getExtras();
if (extras != null) {
@@ -67,6 +71,8 @@ public class WalletActivity extends AppCompatActivity
} else {
throw new IllegalStateException("No extras passed! Panic!");
}
+
+ onProgress(getString(R.string.status_wallet_loading));
showProgress();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@@ -75,7 +81,11 @@ public class WalletActivity extends AppCompatActivity
onProgress(10); // look like we are working!
}
}, 250);
- //Log.d(TAG, "onStart() done.");
+ }
+
+ private void stopWalletService() {
+ releaseWakeLock();
+ disconnectWalletService();
}
private String title = null;
@@ -95,15 +105,13 @@ public class WalletActivity extends AppCompatActivity
@Override
protected void onStop() {
Log.d(TAG, "onStop()");
- releaseWakeLock();
- disconnectWalletService();
- this.synced = false;
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy()");
+ stopWalletService();
super.onDestroy();
}
@@ -129,6 +137,7 @@ public class WalletActivity extends AppCompatActivity
recyclerView.setAdapter(adapter);
setTitle(getString(R.string.status_wallet_loading));
+ startWalletService();
//Log.d(TAG, "onCreate() done.");
}
@@ -136,33 +145,34 @@ public class WalletActivity extends AppCompatActivity
private boolean synced = false;
private void updateStatus(Wallet wallet) {
+ Log.d(TAG, "updateStatus()");
setActivityTitle(wallet);
final TextView balanceView = (TextView) findViewById(R.id.tvBalance);
final TextView unlockedView = (TextView) findViewById(R.id.tvUnlockedBalance);
final TextView syncProgressView = (TextView) findViewById(R.id.tvBlockHeightProgress);
final TextView connectionStatusView = (TextView) findViewById(R.id.tvConnectionStatus);
-
- //Wallet wallet = getWallet();
balanceView.setText(Wallet.getDisplayAmount(wallet.getBalance()));
unlockedView.setText(Wallet.getDisplayAmount(wallet.getUnlockedBalance()));
String sync = "";
- // TODO: getConnectionStatus() blocks as it tries to connect - this is bad in the UI thread!
- if (wallet.getConnectionStatus() == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
+ if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
+ Wallet.ConnectionStatus daemonConnected = mBoundService.getConnectionStatus();
+ if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
+ long daemonHeight = mBoundService.getDaemonHeight();
if (!wallet.isSynchronized()) {
- long n = wallet.getDaemonBlockChainHeight() - wallet.getBlockChainHeight();
+ long n = daemonHeight - wallet.getBlockChainHeight();
sync = n + " " + getString(R.string.status_remaining);
if (firstBlock == 0) {
firstBlock = wallet.getBlockChainHeight();
}
- int x = 100 - Math.round(100f * n / (1f * wallet.getDaemonBlockChainHeight() - firstBlock));
- //Log.d(TAG, n + "/" + (wallet.getDaemonBlockChainHeight() - firstBlock));
+ int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
onProgress(getString(R.string.status_syncing) + " " + sync);
+ if (x == 0) x = -1;
onProgress(x);
} else {
sync = getString(R.string.status_synced) + ": " + wallet.getBlockChainHeight();
if (!synced) {
hideProgress();
- saveWallet(); // save ONLY on first sync
+ saveWallet(); // save on first sync
// the usual use case is:
// open the wallet, wait for sync, check balance, close app
// even if we wait for new transactions, they will be synced and saved next time
@@ -174,7 +184,7 @@ public class WalletActivity extends AppCompatActivity
}
String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));
syncProgressView.setText(sync);
- connectionStatusView.setText(net + " " + wallet.getConnectionStatus().toString().substring(17));
+ connectionStatusView.setText(net + " " + daemonConnected.toString().substring(17));
}
@Override
@@ -364,7 +374,12 @@ public class WalletActivity extends AppCompatActivity
runOnUiThread(new Runnable() {
public void run() {
ProgressBar progress = (ProgressBar) findViewById(R.id.pbProgress);
- progress.setProgress(n);
+ if (n >= 0) {
+ progress.setIndeterminate(false);
+ progress.setProgress(n);
+ } else {
+ progress.setIndeterminate(true);
+ }
}
});
}
diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java b/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java
new file mode 100644
index 0000000..a214ded
--- /dev/null
+++ b/app/src/main/java/com/m2049r/xmrwallet/service/MoneroHandlerThread.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ * 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.service;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+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)
+ */
+public class MoneroHandlerThread extends Thread {
+ // from src/cryptonote_config.h
+ static public final long THREAD_STACK_SIZE = 5 * 1024 * 1024;
+ int mPriority;
+ int mTid = -1;
+ Looper mLooper;
+
+ public MoneroHandlerThread(String name) {
+ super(null, null, name, THREAD_STACK_SIZE);
+ mPriority = Process.THREAD_PRIORITY_DEFAULT;
+ }
+
+ /**
+ * Constructs a MoneroHandlerThread.
+ *
+ * @param name
+ * @param priority The priority to run the thread at. The value supplied must be from
+ * {@link android.os.Process} and not from java.lang.Thread.
+ */
+ public MoneroHandlerThread(String name, int priority) {
+ super(null, null, name, THREAD_STACK_SIZE);
+ mPriority = priority;
+ }
+
+ /**
+ * Call back method that can be explicitly overridden if needed to execute some
+ * setup before Looper loops.
+ */
+
+ protected void onLooperPrepared() {
+ }
+
+ @Override
+ public void run() {
+ mTid = Process.myTid();
+ Looper.prepare();
+ synchronized (this) {
+ mLooper = Looper.myLooper();
+ notifyAll();
+ }
+ Process.setThreadPriority(mPriority);
+ onLooperPrepared();
+ Looper.loop();
+ mTid = -1;
+ }
+
+ /**
+ * This method returns the Looper associated with this thread. If this thread not been started
+ * or for any reason is isAlive() returns false, this method will return null. If this thread
+ * has been started, this method will block until the looper has been initialized.
+ *
+ * @return The looper.
+ */
+ public Looper getLooper() {
+ if (!isAlive()) {
+ return null;
+ }
+
+ // If the thread has been started, wait until the looper has been created.
+ synchronized (this) {
+ while (isAlive() && mLooper == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mLooper;
+ }
+
+ /**
+ * Quits the handler thread's looper.
+ *
+ * Causes the handler thread's looper to terminate without processing any
+ * more messages in the message queue.
+ *
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
+ *
+ * Using this method may be unsafe because some messages may not be delivered
+ * before the looper terminates. Consider using {@link #quitSafely} instead to ensure
+ * that all pending work is completed in an orderly manner.
+ *
+ *
+ * @return True if the looper looper has been asked to quit or false if the
+ * thread had not yet started running.
+ * @see #quitSafely
+ */
+ public boolean quit() {
+ Looper looper = getLooper();
+ if (looper != null) {
+ looper.quit();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Quits the handler thread's looper safely.
+ *
+ * Causes the handler thread's looper to terminate as soon as all remaining messages
+ * in the message queue that are already due to be delivered have been handled.
+ * Pending delayed messages with due times in the future will not be delivered.
+ *
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
+ *
+ * If the thread has not been started or has finished (that is if
+ * {@link #getLooper} returns null), then false is returned.
+ * Otherwise the looper is asked to quit and true is returned.
+ *
+ *
+ * @return True if the looper looper has been asked to quit or false if the
+ * thread had not yet started running.
+ */
+ public boolean quitSafely() {
+ Looper looper = getLooper();
+ if (looper != null) {
+ looper.quitSafely();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the identifier of this thread. See Process.myTid().
+ */
+ public int getThreadId() {
+ return mTid;
+ }
+}
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 4cdc4f0..7fd17d2 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
@@ -21,7 +21,6 @@ import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -29,6 +28,7 @@ import android.os.Process;
import android.util.Log;
import com.m2049r.xmrwallet.R;
+import com.m2049r.xmrwallet.model.TransactionHistory;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletListener;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -97,15 +97,27 @@ public class WalletService extends Service {
}
long lastBlockTime = 0;
+ int lastTxCount = 0;
public void newBlock(long height) {
if (wallet == null) throw new IllegalStateException("No wallet!");
// don't flood with an update for every block ...
if (lastBlockTime < System.currentTimeMillis() - 2000) {
- Log.d(TAG, "newBlock() @" + height + "with observer " + observer);
+ Log.d(TAG, "newBlock() @" + height + " with observer " + observer);
lastBlockTime = System.currentTimeMillis();
if (observer != null) {
- observer.onRefreshed(wallet, false);
+ boolean fullRefresh = false;
+ updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
+ if (!wallet.isSynchronized()) {
+ // we want to see our transactions as they come in
+ wallet.getHistory().refresh();
+ int txCount = wallet.getHistory().getCount();
+ if (txCount > lastTxCount) {
+ lastTxCount = txCount;
+ fullRefresh = true;
+ }
+ }
+ observer.onRefreshed(wallet, fullRefresh);
}
}
}
@@ -118,17 +130,61 @@ public class WalletService extends Service {
public void refreshed() {
if (wallet == null) throw new IllegalStateException("No wallet!");
- Log.d(TAG, "refreshed() " + wallet.getBalance() + " sync=" + wallet.isSynchronized() + "with observer " + observer);
+ Log.d(TAG, "refreshed() " + wallet.getName() + " " + wallet.getBalance() + " sync=" + wallet.isSynchronized() + " with observer " + observer);
if (updated) {
if (observer != null) {
- wallet.getHistory().refresh();
+ Log.d(TAG, "refreshed() A");
+ updateDaemonState(wallet, 0);
+ Log.d(TAG, "refreshed() B");
+ TransactionHistory history = wallet.getHistory();
+ Log.d(TAG, "refreshed() C " + history.getCount());
+ history.refresh();
+ Log.d(TAG, "refreshed() D " + history.getCount());
+ Log.d(TAG, "refreshed() E");
observer.onRefreshed(wallet, true);
+ Log.d(TAG, "refreshed() D");
updated = false;
}
}
}
}
+ private long lastDaemonStatusUpdate = 0;
+ private long daemonHeight = 0;
+ private Wallet.ConnectionStatus connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
+ private static final long STATUS_UPDATE_INTERVAL = 120000; // 120s (blocktime)
+
+ private void updateDaemonState(Wallet wallet, long height) {
+ long t = System.currentTimeMillis();
+ if (height > 0) { // if we get a height, we are connected
+ daemonHeight = height;
+ connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Connected;
+ lastDaemonStatusUpdate = t;
+ } else {
+ if (t - lastDaemonStatusUpdate > STATUS_UPDATE_INTERVAL) {
+ lastDaemonStatusUpdate = t;
+ // these calls really connect to the daemon - wasting time
+ daemonHeight = wallet.getDaemonBlockChainHeight();
+ if (daemonHeight > 0) {
+ // if we get a valid height, the obviously we are connected
+ connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Connected;
+ } else {
+ // TODO: or connectionStatus = wallet.getConnectionStatus(); ?
+ connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
+ }
+ }
+ }
+ //Log.d(TAG, "updated daemon status: " + daemonHeight + "/" + connectionStatus.toString());
+ }
+
+ public long getDaemonHeight() {
+ return this.daemonHeight;
+ }
+
+ public Wallet.ConnectionStatus getConnectionStatus() {
+ return this.connectionStatus;
+ }
+
/////////////////////////////////////////////
// communication back to client (activity) //
/////////////////////////////////////////////
@@ -217,7 +273,7 @@ public class WalletService extends Service {
// We are using a HandlerThread and a Looper to avoid loading and closing
// concurrency
- HandlerThread thread = new HandlerThread("WalletService",
+ MoneroHandlerThread thread = new MoneroHandlerThread("WalletService",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
@@ -294,14 +350,13 @@ public class WalletService extends Service {
Wallet aWallet = loadWallet(walletName, walletPassword);
listener = new MyWalletListener(aWallet);
listener.start();
- showProgress(95);
+ showProgress(100);
}
+ showProgress(getString(R.string.status_wallet_connecting));
+ showProgress(-1);
+ // if we try to refresh the history here we get occasional segfaults!
+ // doesnt matter since we update as soon as we get a new block anyway
Log.d(TAG, "start() done");
- if (observer != null) {
- Wallet myWallet = getWallet();
- myWallet.getHistory().refresh();
- observer.onRefreshed(myWallet, true);
- }
}
public void stop() {
@@ -309,8 +364,13 @@ public class WalletService extends Service {
setObserver(null); // in case it was not reset already
if (listener != null) {
listener.stop();
+ Wallet myWallet = getWallet();
+// if (!myWallet.isSynchronized()) { // save only if NOT synced (to continue later)
+// Log.d(TAG, "stop() saving");
+// myWallet.store();
+// }
Log.d(TAG, "stop() closing");
- listener.getWallet().close();
+ myWallet.close();
Log.d(TAG, "stop() closed");
listener = null;
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0ad67e4..e616d69 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -11,11 +11,12 @@
Loading Wallet List
Loading Wallet …
Saving Wallet
+ Connecting …
Password for
Bad password!
Daemon address must be set!
Daemon type does not fit to wallet!
- Warning: cannot reach daemon!
+ Cannot connect to daemon!
Something\'s wrong!
Amount
diff --git a/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a
index 875ed7d..a3c17e0 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a and b/external-libs/monero/lib/armeabi-v7a/libblockchain_db.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libblocks.a b/external-libs/monero/lib/armeabi-v7a/libblocks.a
index 62a86e4..4f2accf 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libblocks.a and b/external-libs/monero/lib/armeabi-v7a/libblocks.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libcncrypto.a b/external-libs/monero/lib/armeabi-v7a/libcncrypto.a
index bf59079..3b9769b 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libcncrypto.a and b/external-libs/monero/lib/armeabi-v7a/libcncrypto.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libcommon.a b/external-libs/monero/lib/armeabi-v7a/libcommon.a
index 009d6d3..33720a0 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libcommon.a and b/external-libs/monero/lib/armeabi-v7a/libcommon.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a
index d578cc9..8d0e4fe 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_basic.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a
index e462875..92208c7 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_core.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a
index 496c699..5d31a89 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a and b/external-libs/monero/lib/armeabi-v7a/libcryptonote_protocol.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a
index 06db80a..568d67b 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a and b/external-libs/monero/lib/armeabi-v7a/libdaemonizer.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libeasylogging.a b/external-libs/monero/lib/armeabi-v7a/libeasylogging.a
deleted file mode 100644
index 028a12c..0000000
Binary files a/external-libs/monero/lib/armeabi-v7a/libeasylogging.a and /dev/null differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libepee.a b/external-libs/monero/lib/armeabi-v7a/libepee.a
index c24969f..40dd849 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libepee.a and b/external-libs/monero/lib/armeabi-v7a/libepee.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/liblmdb.a b/external-libs/monero/lib/armeabi-v7a/liblmdb.a
index 18b69c3..dd38c1e 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/liblmdb.a and b/external-libs/monero/lib/armeabi-v7a/liblmdb.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a b/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a
index 341c329..dba8084 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a and b/external-libs/monero/lib/armeabi-v7a/libminiupnpc.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libmnemonics.a b/external-libs/monero/lib/armeabi-v7a/libmnemonics.a
index 4589a99..6df688b 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libmnemonics.a and b/external-libs/monero/lib/armeabi-v7a/libmnemonics.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libp2p.a b/external-libs/monero/lib/armeabi-v7a/libp2p.a
index e1ea1c4..6076496 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libp2p.a and b/external-libs/monero/lib/armeabi-v7a/libp2p.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libringct.a b/external-libs/monero/lib/armeabi-v7a/libringct.a
index 6bc6602..21d235b 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libringct.a and b/external-libs/monero/lib/armeabi-v7a/libringct.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/librpc.a b/external-libs/monero/lib/armeabi-v7a/librpc.a
index 71025e4..8136509 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/librpc.a and b/external-libs/monero/lib/armeabi-v7a/librpc.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libunbound.a b/external-libs/monero/lib/armeabi-v7a/libunbound.a
index 0a1f8c9..a2ff8df 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libunbound.a and b/external-libs/monero/lib/armeabi-v7a/libunbound.a differ
diff --git a/external-libs/monero/lib/armeabi-v7a/libwallet.a b/external-libs/monero/lib/armeabi-v7a/libwallet.a
index 715b96e..2959da8 100644
Binary files a/external-libs/monero/lib/armeabi-v7a/libwallet.a and b/external-libs/monero/lib/armeabi-v7a/libwallet.a differ