diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 577db59..f0fbceb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -102,7 +102,8 @@
-
+
+
drawerItems = new ArrayList<>();
for (Label label : messageRepo.getLabels()) {
@@ -414,11 +416,24 @@ public class MessageListActivity extends AppCompatActivity
super.onStop();
}
- private class IncomingHandler extends Handler {
+ private static class IncomingHandler extends Handler {
+ private WeakReference accountHeaderRef;
+
+ private IncomingHandler() {
+ accountHeaderRef = new WeakReference<>(null);
+ }
+
+ public void updateAccountHeader(AccountHeader accountHeader){
+ accountHeaderRef = new WeakReference<>(accountHeader);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BitmessageService.MSG_CREATE_IDENTITY: {
+ AccountHeader accountHeader = accountHeaderRef.get();
+ if (accountHeader == null) break;
+
Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
if (data instanceof BitmessageAddress) {
BitmessageAddress identity = (BitmessageAddress) data;
diff --git a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java
index 8f87f8a..17ffe41 100644
--- a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java
+++ b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java
@@ -37,13 +37,13 @@ import android.widget.TextView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import ch.dissem.apps.abit.synchronization.BitmessageService;
+import ch.dissem.apps.abit.service.BitmessageService;
import ch.dissem.bitmessage.entity.BitmessageAddress;
-import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS;
-import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_ADD_CONTACT;
-import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE;
-import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT;
+import static ch.dissem.apps.abit.service.BitmessageService.DATA_FIELD_ADDRESS;
+import static ch.dissem.apps.abit.service.BitmessageService.MSG_ADD_CONTACT;
+import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE;
+import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT;
public class OpenBitmessageLinkActivity extends AppCompatActivity {
private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class);
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java
index 60eb282..21ff505 100644
--- a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java
+++ b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java
@@ -14,7 +14,7 @@ public abstract class AbstractNotification {
public AbstractNotification(Context ctx) {
- this.ctx = ctx;
+ this.ctx = ctx.getApplicationContext();
this.manager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
}
@@ -23,6 +23,10 @@ public abstract class AbstractNotification {
*/
protected abstract int getNotificationId();
+ public Notification getNotification() {
+ return notification;
+ }
+
public void show() {
manager.notify(getNotificationId(), notification);
}
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java
index d14aa78..c64185e 100644
--- a/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java
+++ b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java
@@ -1,6 +1,7 @@
package ch.dissem.apps.abit.notification;
import android.content.Context;
+import android.support.annotation.StringRes;
import android.support.v7.app.NotificationCompat;
import ch.dissem.apps.abit.R;
@@ -20,14 +21,14 @@ public class ErrorNotification extends AbstractNotification {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
- public ErrorNotification setWarning(int resId, Object... args) {
+ public ErrorNotification setWarning(@StringRes int resId, Object... args) {
builder.setSmallIcon(R.drawable.ic_notification_warning)
.setContentText(ctx.getString(resId, args));
notification = builder.build();
return this;
}
- public ErrorNotification setError(int resId, Object... args) {
+ public ErrorNotification setError(@StringRes int resId, Object... args) {
builder.setSmallIcon(R.drawable.ic_notification_error)
.setContentText(ctx.getString(resId, args));
notification = builder.build();
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java
index 3530c2a..bff6e92 100644
--- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java
+++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java
@@ -12,7 +12,6 @@ import java.util.TimerTask;
import ch.dissem.apps.abit.MessageListActivity;
import ch.dissem.apps.abit.R;
-import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.utils.Property;
@@ -26,7 +25,7 @@ public class NetworkNotification extends AbstractNotification {
private NotificationCompat.Builder builder;
public NetworkNotification(Context ctx, BitmessageContext bmc) {
- super(ctx.getApplicationContext());
+ super(ctx);
this.bmc = bmc;
builder = new NotificationCompat.Builder(ctx);
builder.setSmallIcon(R.drawable.ic_notification_full_node)
@@ -34,6 +33,7 @@ public class NetworkNotification extends AbstractNotification {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
+ @Override
public Notification getNotification() {
update();
return notification;
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java
new file mode 100644
index 0000000..f917a83
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java
@@ -0,0 +1,38 @@
+package ch.dissem.apps.abit.notification;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.NotificationCompat;
+
+import ch.dissem.apps.abit.MessageListActivity;
+import ch.dissem.apps.abit.R;
+
+/**
+ * Ongoing notification while proof of work is in progress.
+ */
+public class ProofOfWorkNotification extends AbstractNotification {
+ public static final int ONGOING_NOTIFICATION_ID = 3;
+
+ public ProofOfWorkNotification(Context ctx) {
+ super(ctx);
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
+
+ Intent showMessageIntent = new Intent(ctx, MessageListActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setUsesChronometer(true)
+ .setSmallIcon(R.drawable.ic_notification_proof_of_work)
+ .setContentTitle(ctx.getString(R.string.proof_of_work_title))
+ .setContentText(ctx.getString(R.string.proof_of_work_text))
+ .setContentIntent(pendingIntent);
+
+ notification = builder.build();
+ }
+
+ @Override
+ protected int getNotificationId() {
+ return ONGOING_NOTIFICATION_ID;
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java
similarity index 67%
rename from app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java
rename to app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java
index bb15042..e52eb95 100644
--- a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java
+++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java
@@ -1,25 +1,21 @@
-package ch.dissem.apps.abit.synchronization;
+package ch.dissem.apps.abit.service;
import android.app.Service;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.preference.PreferenceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
+import java.lang.ref.WeakReference;
import ch.dissem.apps.abit.notification.NetworkNotification;
-import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
@@ -33,7 +29,6 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF
public class BitmessageService extends Service {
public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class);
- public static final int MSG_SYNC = 2;
public static final int MSG_CREATE_IDENTITY = 10;
public static final int MSG_SUBSCRIBE = 20;
public static final int MSG_ADD_CONTACT = 21;
@@ -68,7 +63,7 @@ public class BitmessageService extends Service {
if (bmc == null) {
bmc = Singleton.getBitmessageContext(this);
notification = new NetworkNotification(this, bmc);
- messenger = new Messenger(new IncomingHandler());
+ messenger = new Messenger(new IncomingHandler(this));
}
}
}
@@ -93,7 +88,13 @@ public class BitmessageService extends Service {
return messenger.getBinder();
}
- private class IncomingHandler extends Handler {
+ private static class IncomingHandler extends Handler {
+ private WeakReference service;
+
+ private IncomingHandler(BitmessageService service) {
+ this.service = new WeakReference<>(service);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -119,45 +120,6 @@ public class BitmessageService extends Service {
}
break;
}
- case MSG_SYNC: {
- LOG.info("Synchronizing Bitmessage");
- // If the Bitmessage context acts as a full node, synchronization isn't necessary
- if (bmc.isRunning()) break;
-
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
- BitmessageService.this);
-
- String trustedNode = preferences.getString("trusted_node", null);
- if (trustedNode == null) break;
- trustedNode = trustedNode.trim();
- if (trustedNode.isEmpty()) break;
-
- int port;
- if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$")) {
- int index = trustedNode.lastIndexOf(':');
- String portString = trustedNode.substring(index + 1);
- trustedNode = trustedNode.substring(0, index);
- try {
- port = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- LOG.error("Invalid port " + portString);
- // TODO: show error as notification
- return;
- }
- } else {
- port = 8444;
- }
- long timeoutInSeconds = preferences.getInt("sync_timeout", 120);
- try {
- LOG.info("Synchronization started");
- bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
- LOG.info("Synchronization finished");
- } catch (UnknownHostException e) {
- LOG.error("Couldn't synchronize", e);
- // TODO: show error as notification
- }
- break;
- }
case MSG_SEND_MESSAGE: {
Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
@@ -182,17 +144,17 @@ public class BitmessageService extends Service {
case MSG_START_NODE:
// TODO: warn user, option to restrict to WiFi
// (I'm not quite sure this can be done here, though)
- startService(new Intent(BitmessageService.this, BitmessageService.class));
+ service.get().startService(new Intent(service.get(), BitmessageService.class));
running = true;
- startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
+ service.get().startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
bmc.startup();
notification.show();
break;
case MSG_STOP_NODE:
bmc.shutdown();
running = false;
- stopForeground(false);
- stopService(new Intent(BitmessageService.this, BitmessageService.class));
+ service.get().stopForeground(false);
+ service.get().stopSelf();
break;
default:
super.handleMessage(msg);
diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java
new file mode 100644
index 0000000..7d86ef8
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java
@@ -0,0 +1,88 @@
+package ch.dissem.apps.abit.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.ref.WeakReference;
+
+import ch.dissem.apps.abit.notification.ProofOfWorkNotification;
+import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine;
+import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
+
+import static ch.dissem.apps.abit.notification.ProofOfWorkNotification.ONGOING_NOTIFICATION_ID;
+
+/**
+ * The Proof of Work Service makes sure POW is done in a foreground process, so it shouldn't be
+ * killed by the system before the nonce is found.
+ */
+public class ProofOfWorkService extends Service {
+ public static final Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class);
+
+ // Object to use as a thread-safe lock
+ private static final Object lock = new Object();
+ private static ProofOfWorkEngine engine;
+
+ @Override
+ public void onCreate() {
+ synchronized (lock) {
+ if (engine == null) {
+ engine = new MultiThreadedPOWEngine();
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new PowBinder(engine, this);
+ }
+
+ public static class PowBinder extends Binder {
+ private final ProofOfWorkEngine engine;
+
+ private PowBinder(ProofOfWorkEngine engine, ProofOfWorkService service) {
+ this.engine = new EngineWrapper(engine, service);
+ }
+
+ public ProofOfWorkEngine getEngine() {
+ return engine;
+ }
+ }
+
+ private static class EngineWrapper implements ProofOfWorkEngine {
+ private final ProofOfWorkNotification notification;
+ private final ProofOfWorkEngine engine;
+ private final WeakReference serviceRef;
+
+ private EngineWrapper(ProofOfWorkEngine engine, ProofOfWorkService service) {
+ this.engine = engine;
+ this.serviceRef = new WeakReference<>(service);
+ this.notification = new ProofOfWorkNotification(service);
+ }
+
+ @Override
+ public void calculateNonce(byte[] initialHash, byte[] target, final Callback callback) {
+ final ProofOfWorkService service = serviceRef.get();
+ service.startService(new Intent(service, ProofOfWorkService.class));
+ service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
+ engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() {
+ @Override
+ public void onNonceCalculated(byte[] nonce) {
+ try {
+ callback.onNonceCalculated(nonce);
+ } finally {
+ service.stopForeground(true);
+ service.stopSelf();
+ }
+ }
+ });
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java b/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java
new file mode 100644
index 0000000..16d2aaa
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java
@@ -0,0 +1,61 @@
+package ch.dissem.apps.abit.service;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import java.util.concurrent.Semaphore;
+
+import ch.dissem.apps.abit.service.ProofOfWorkService.PowBinder;
+import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
+
+import static android.content.Context.BIND_AUTO_CREATE;
+
+/**
+ * Proof of Work engine that uses the Proof of Work service.
+ */
+public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Callback {
+ private final Semaphore semaphore = new Semaphore(1, true);
+ private final Context ctx;
+
+ private byte[] initialHash, targetValue;
+ private Callback callback;
+
+ public ServicePowEngine(Context ctx) {
+ this.ctx = ctx;
+ }
+
+ private ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ ((PowBinder) service).getEngine().calculateNonce(initialHash, targetValue, ServicePowEngine.this);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ semaphore.release();
+ }
+ };
+
+ @Override
+ public void calculateNonce(byte[] initialHash, byte[] targetValue, Callback callback) {
+ try {
+ semaphore.acquire();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ this.initialHash = initialHash;
+ this.targetValue = targetValue;
+ this.callback = callback;
+ ctx.bindService(new Intent(ctx, ProofOfWorkService.class), connection, BIND_AUTO_CREATE);
+ }
+
+ @Override
+ public void onNonceCalculated(byte[] bytes) {
+ callback.onNonceCalculated(bytes);
+ ctx.unbindService(connection);
+ }
+
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java
index f00d3c7..a178519 100644
--- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java
+++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java
@@ -2,9 +2,6 @@ package ch.dissem.apps.abit.service;
import android.content.Context;
-import java.util.Objects;
-
-import ch.dissem.apps.abit.MessageListActivity;
import ch.dissem.apps.abit.listener.MessageListener;
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
import ch.dissem.apps.abit.repository.AndroidInventory;
@@ -15,7 +12,6 @@ import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.ports.MessageRepository;
-import ch.dissem.bitmessage.ports.Security;
import ch.dissem.bitmessage.security.sc.SpongySecurity;
/**
@@ -33,6 +29,7 @@ public class Singleton {
final Context ctx = context.getApplicationContext();
SqlHelper sqlHelper = new SqlHelper(ctx);
bitmessageContext = new BitmessageContext.Builder()
+ .proofOfWorkEngine(new ServicePowEngine(ctx))
.security(new SpongySecurity())
.nodeRegistry(new MemoryNodeRegistry())
.inventory(new AndroidInventory(sqlHelper))
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java
index 4bbc627..7654833 100644
--- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java
@@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import ch.dissem.apps.abit.R;
+import ch.dissem.apps.abit.notification.ErrorNotification;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
@@ -59,8 +61,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
try {
port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
- LOG.error("Invalid port " + portString);
- // TODO: show error as notification
+ new ErrorNotification(getContext())
+ .setError(R.string.error_invalid_sync_port, portString)
+ .show();
return;
}
} else {
@@ -72,8 +75,11 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
LOG.info("Synchronization finished");
} catch (UnknownHostException e) {
- LOG.error("Couldn't synchronize", e);
- // TODO: show error as notification
+ new ErrorNotification(getContext())
+ .setError(R.string.error_invalid_sync_host)
+ .show();
+ } catch (RuntimeException e) {
+ LOG.error(e.getMessage(), e);
}
}
}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 383adde..7020bf7 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -42,4 +42,8 @@
Stream %1$d: %2$d Verbindungen
Getrennt
Verbindung wird aufgebaut…
+ Warnung: dies könnte das Gerät erwärmen bis die Batterie leer ist.
+ Proof of Work
+ Synchronisation fehlgeschlagen: der vertrauenswürdige Knoten konnte nicht erreicht werden.
+ Ungültiger Port in den Synchronisationseinstellungen: %s
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b493dc2..ed1cd40 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -42,4 +42,8 @@
Send
Disconnected
Connecting…
+ Proof of Work
+ Warning: This might heat your device until the battery\'s dead.
+ Invalid port in synchronization settings: %s
+ Synchronization failed: Trusted node could not be reached.