From f5bf5c8bca6fdb665bd2644ef5bbd4a8848d3532 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 23 Oct 2015 22:40:09 +0200 Subject: [PATCH] Moving Bitmessage context into a foreground service (work in progress) --- app/src/main/AndroidManifest.xml | 1 + .../apps/abit/AbstractItemListFragment.java | 19 +- .../apps/abit/MessageDetailFragment.java | 19 +- .../dissem/apps/abit/MessageListActivity.java | 115 +++++++++-- .../dissem/apps/abit/MessageListFragment.java | 9 +- .../apps/abit/OpenBitmessageLinkActivity.java | 79 +++++++- .../apps/abit/SubscriptionDetailFragment.java | 3 +- .../apps/abit/SubscriptionListFragment.java | 6 +- .../notification/NetworkNotification.java | 6 +- .../notification/NewMessageNotification.java | 3 +- .../repository/AndroidMessageRepository.java | 21 +- .../dissem/apps/abit/service/Singleton.java | 67 +++++-- .../synchronization/BitmessageService.java | 186 ++++++++++++++++++ .../abit/synchronization/SyncService.java | 50 +---- 14 files changed, 441 insertions(+), 143 deletions(-) create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f635f00..577db59 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -102,6 +102,7 @@ + extends ListFragment { public void onItemSelected(Object plaintext) { } }; - protected BitmessageContext bmc; /** * The fragment's current callback object, which is notified of list item * clicks. @@ -59,13 +57,6 @@ public abstract class AbstractItemListFragment extends ListFragment { abstract void updateList(Label label); - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - bmc = Singleton.getBitmessageContext(getActivity()); - } - @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -89,15 +80,15 @@ public abstract class AbstractItemListFragment extends ListFragment { } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); + public void onAttach(Context context) { + super.onAttach(context); // Activities containing this fragment must implement its callbacks. - if (!(activity instanceof ListSelectionListener)) { + if (!(context instanceof ListSelectionListener)) { throw new IllegalStateException("Activity must implement fragment's callbacks."); } - callbacks = (ListSelectionListener) activity; + callbacks = (ListSelectionListener) context; } @Override diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java index 4652f6b..c43ae5f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java @@ -6,12 +6,15 @@ import android.support.v4.app.Fragment; import android.view.*; import android.widget.ImageView; import android.widget.TextView; + import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.util.Drawables; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.MessageRepository; + import com.mikepenz.google_material_typeface_library.GoogleMaterial; import java.util.Iterator; @@ -84,7 +87,7 @@ public class MessageDetailFragment extends Fragment { } } if (removed) { - Singleton.getBitmessageContext(inflater.getContext()).messages().save(item); + Singleton.getMessageRepository(inflater.getContext()).save(item); } return rootView; } @@ -103,7 +106,7 @@ public class MessageDetailFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem menuItem) { - BitmessageContext bmc = Singleton.getBitmessageContext(getActivity()); + MessageRepository messageRepo = Singleton.getMessageRepository(getContext()); switch (menuItem.getItemId()) { case R.id.reply: Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); @@ -113,21 +116,21 @@ public class MessageDetailFragment extends Fragment { return true; case R.id.delete: if (isInTrash(item)) { - bmc.messages().remove(item); + messageRepo.remove(item); } else { item.getLabels().clear(); - item.addLabels(bmc.messages().getLabels(Label.Type.TRASH)); - bmc.messages().save(item); + item.addLabels(messageRepo.getLabels(Label.Type.TRASH)); + messageRepo.save(item); } getActivity().onBackPressed(); return true; case R.id.mark_unread: - item.addLabels(bmc.messages().getLabels(Label.Type.UNREAD)); - bmc.messages().save(item); + item.addLabels(messageRepo.getLabels(Label.Type.UNREAD)); + messageRepo.save(item); return true; case R.id.archive: item.getLabels().clear(); - bmc.messages().save(item); + messageRepo.save(item); return true; default: return false; diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java index f488cdf..3899a60 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -2,9 +2,17 @@ package ch.dissem.apps.abit; import android.accounts.Account; import android.accounts.AccountManager; +import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; 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.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -38,12 +46,17 @@ import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ListSelectionListener; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; +import ch.dissem.apps.abit.synchronization.BitmessageService; import ch.dissem.apps.abit.synchronization.SyncService; -import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.AddressRepository; +import ch.dissem.bitmessage.ports.MessageRepository; +import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_START_NODE; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_STOP_NODE; import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY; @@ -79,15 +92,36 @@ public class MessageListActivity extends AppCompatActivity */ private boolean twoPane; + private Messenger messenger = new Messenger(new IncomingHandler()); + private Messenger service; + private boolean bound; + private ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + MessageListActivity.this.service = new Messenger(service); + MessageListActivity.this.bound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + service = null; + bound = false; + } + }; + private AccountHeader accountHeader; - private BitmessageContext bmc; private Label selectedLabel; + private MessageRepository messageRepo; + private AddressRepository addressRepo; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - bmc = Singleton.getBitmessageContext(this); - selectedLabel = bmc.messages().getLabels().get(0); + messageRepo = Singleton.getMessageRepository(this); + addressRepo = Singleton.getAddressRepository(this); + + selectedLabel = messageRepo.getLabels().get(0); setContentView(R.layout.activity_message_list); @@ -152,7 +186,7 @@ public class MessageListActivity extends AppCompatActivity private void createDrawer(Toolbar toolbar) { final ArrayList profiles = new ArrayList<>(); - for (BitmessageAddress identity : bmc.addresses().getIdentities()) { + for (BitmessageAddress identity : addressRepo.getIdentities()) { LOG.info("Adding identity " + identity.getAddress()); profiles.add(new ProfileDrawerItem() .withIcon(new Identicon(identity)) @@ -183,16 +217,12 @@ public class MessageListActivity extends AppCompatActivity @Override public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) { if (profile.getIdentifier() == ADD_IDENTITY) { - BitmessageAddress identity = bmc.createIdentity(false); - IProfile newProfile = new ProfileDrawerItem() - .withName(identity.toString()) - .withEmail(identity.getAddress()) - .withTag(identity); - if (accountHeader.getProfiles() != null) { - //we know that there are 2 setting elements. set the new profile above them ;) - accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); - } else { - accountHeader.addProfiles(newProfile); + try { + Message message = Message.obtain(null, BitmessageService.MSG_CREATE_IDENTITY); + message.replyTo = messenger; + service.send(message); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); } } // false if it should close the drawer @@ -202,7 +232,7 @@ public class MessageListActivity extends AppCompatActivity .build(); ArrayList drawerItems = new ArrayList<>(); - for (Label label : bmc.messages().getLabels()) { + for (Label label : messageRepo.getLabels()) { PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag(label); switch (label.getType()) { case INBOX: @@ -254,13 +284,21 @@ public class MessageListActivity extends AppCompatActivity @Override public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { if (isChecked) { - startService(new Intent(MessageListActivity.this, SyncService.class)); + try { + service.send(Message.obtain(null, MSG_START_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } else { - stopService(new Intent(MessageListActivity.this, SyncService.class)); + try { + service.send(Message.obtain(null, MSG_STOP_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } } }) - .withChecked(SyncService.isRunning()) + .withChecked(BitmessageService.isRunning()) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override @@ -303,7 +341,7 @@ public class MessageListActivity extends AppCompatActivity ((MessageListFragment) getSupportFragmentManager() .findFragmentById(R.id.item_list)).updateList(selectedLabel); } else { - MessageListFragment listFragment = new MessageListFragment(getApplicationContext()); + MessageListFragment listFragment = new MessageListFragment(); changeList(listFragment); listFragment.updateList(selectedLabel); } @@ -360,4 +398,41 @@ public class MessageListActivity extends AppCompatActivity return selectedLabel; } + @Override + protected void onStart() { + super.onStart(); + bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + if (bound) { + unbindService(connection); + bound = false; + } + super.onStop(); + } + + private class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case BitmessageService.MSG_CREATE_IDENTITY: + BitmessageAddress identity = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); + IProfile newProfile = new ProfileDrawerItem() + .withName(identity.toString()) + .withEmail(identity.getAddress()) + .withTag(identity); + if (accountHeader.getProfiles() != null) { + //we know that there are 2 setting elements. set the new profile above them ;) + accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); + } else { + accountHeader.addProfiles(newProfile); + } + break; + default: + super.handleMessage(msg); + } + } + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java index 5de7c7e..802b901 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java @@ -44,11 +44,6 @@ public class MessageListFragment extends AbstractItemListFragment { public MessageListFragment() { } - @SuppressLint("ValidFragment") - public MessageListFragment(Context ctx) { - bmc = Singleton.getBitmessageContext(ctx); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,7 +68,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, - bmc.messages().findMessages(label)) { + Singleton.getMessageRepository(getContext()).findMessages(label)) { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { @@ -138,7 +133,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { case R.id.empty_trash: if (currentLabel.getType() != Label.Type.TRASH) return true; - MessageRepository repo = bmc.messages(); + MessageRepository repo = Singleton.getMessageRepository(getContext()); for (Plaintext message : repo.findMessages(currentLabel)) { repo.remove(message); } 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 cff4772..8f87f8a 100644 --- a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java @@ -17,19 +17,52 @@ package ch.dissem.apps.abit; import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.net.Uri; import android.os.Bundle; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Switch; import android.widget.TextView; -import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.BitmessageContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.dissem.apps.abit.synchronization.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; + public class OpenBitmessageLinkActivity extends AppCompatActivity { + private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class); + + private Messenger service; + private boolean bound; + private ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + OpenBitmessageLinkActivity.this.service = new Messenger(service); + OpenBitmessageLinkActivity.this.bound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + service = null; + bound = false; + } + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -70,14 +103,29 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity { ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - BitmessageContext bmc = Singleton.getBitmessageContext(OpenBitmessageLinkActivity.this); BitmessageAddress bmAddress = new BitmessageAddress(address); bmAddress.setAlias(label.getText().toString()); - if (subscribe.isChecked()) { - bmc.addSubscribtion(bmAddress); - } - if (importContact.isChecked()) { - bmc.addContact(bmAddress); + + final int what; + if (subscribe.isChecked() && importContact.isChecked()) + what = MSG_SUBSCRIBE_AND_ADD_CONTACT; + else if (subscribe.isChecked()) + what = MSG_SUBSCRIBE; + else if (importContact.isChecked()) + what = MSG_ADD_CONTACT; + else + what = 0; + + if (what != 0) { + try { + Message message = Message.obtain(null, what); + Bundle bundle = new Bundle(); + bundle.putSerializable(DATA_FIELD_ADDRESS, bmAddress); + message.setData(bundle); + service.send(message); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } setResult(Activity.RESULT_OK); finish(); @@ -110,4 +158,19 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity { return new String[0]; } } + + @Override + protected void onStart() { + super.onStart(); + bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + if (bound) { + unbindService(connection); + bound = false; + } + super.onStop(); + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java index fa16aec..0fa31e8 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java @@ -27,6 +27,7 @@ import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; + import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; @@ -115,7 +116,7 @@ public class SubscriptionDetailFragment extends Fragment { @Override public void onPause() { - Singleton.getBitmessageContext(getActivity()).addresses().save(item); + Singleton.getAddressRepository(getContext()).save(item); super.onPause(); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java b/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java index a3c7b00..18fa320 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java @@ -24,6 +24,8 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; + +import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.valueobject.Label; @@ -32,7 +34,7 @@ import java.util.Comparator; import java.util.List; /** - * Created by chris on 06.09.15. + * Fragment that shows a list of all contacts, the ones we subscribed to first. */ public class SubscriptionListFragment extends AbstractItemListFragment<BitmessageAddress> { @Override @@ -43,7 +45,7 @@ public class SubscriptionListFragment extends AbstractItemListFragment<Bitmessag } public void updateList() { - List<BitmessageAddress> addresses = bmc.addresses().getContacts(); + List<BitmessageAddress> addresses = Singleton.getAddressRepository(getContext()).getContacts(); Collections.sort(addresses, new Comparator<BitmessageAddress>() { /** * Yields the following order: 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 a013602..3530c2a 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 @@ -25,9 +25,9 @@ public class NetworkNotification extends AbstractNotification { private final BitmessageContext bmc; private NotificationCompat.Builder builder; - public NetworkNotification(Context ctx) { - super(ctx); - bmc = Singleton.getBitmessageContext(ctx); + public NetworkNotification(Context ctx, BitmessageContext bmc) { + super(ctx.getApplicationContext()); + this.bmc = bmc; builder = new NotificationCompat.Builder(ctx); builder.setSmallIcon(R.drawable.ic_notification_full_node) .setContentTitle(ctx.getString(R.string.bitmessage_full_node)) diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java index ee10bf1..2bb63df 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.Plaintext; import static ch.dissem.apps.abit.util.Drawables.toBitmap; public class NewMessageNotification extends AbstractNotification { + public static final int NEW_MESSAGE_NOTIFICATION_ID = 1; private static final StyleSpan SPAN_EMPHASIS = new StyleSpan(Typeface.BOLD); public NewMessageNotification(Context ctx) { @@ -76,6 +77,6 @@ public class NewMessageNotification extends AbstractNotification { @Override protected int getNotificationId() { - return 1; + return NEW_MESSAGE_NOTIFICATION_ID; } } diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java index eba1b0d..83293e7 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java @@ -23,11 +23,13 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.utils.Encode; @@ -45,7 +47,7 @@ import static ch.dissem.apps.abit.repository.SqlHelper.join; /** * {@link MessageRepository} implementation using the Android SQL API. */ -public class AndroidMessageRepository implements MessageRepository, InternalContext.ContextHolder { +public class AndroidMessageRepository implements MessageRepository { private static final Logger LOG = LoggerFactory.getLogger(AndroidMessageRepository.class); private static final String TABLE_NAME = "Message"; @@ -71,16 +73,13 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont private static final String LBL_COLUMN_ORDER = "ord"; private final SqlHelper sql; private final Context ctx; - private InternalContext bmc; + + private final AddressRepository addressRepo; public AndroidMessageRepository(SqlHelper sql, Context ctx) { this.sql = sql; this.ctx = ctx; - } - - @Override - public void setContext(InternalContext context) { - bmc = context; + this.addressRepo = Singleton.getAddressRepository(ctx); } @Override @@ -230,8 +229,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont long id = c.getLong(c.getColumnIndex(COLUMN_ID)); builder.id(id); builder.IV(new InventoryVector(iv)); - builder.from(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); - builder.to(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); + builder.from(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); + builder.to(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT))); builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED))); builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS)))); @@ -257,12 +256,12 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont // save from address if necessary if (message.getId() == null) { - BitmessageAddress savedAddress = bmc.getAddressRepo().getAddress(message.getFrom().getAddress()); + BitmessageAddress savedAddress = addressRepo.getAddress(message.getFrom().getAddress()); if (savedAddress == null || savedAddress.getPrivateKey() == null) { if (savedAddress != null && savedAddress.getAlias() != null) { message.getFrom().setAlias(savedAddress.getAlias()); } - bmc.getAddressRepo().save(message.getFrom()); + addressRepo.save(message.getFrom()); } } 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 220dc36..469d1db 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,6 +2,7 @@ package ch.dissem.apps.abit.service; import android.content.Context; +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; @@ -9,35 +10,24 @@ import ch.dissem.apps.abit.repository.AndroidMessageRepository; import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.bitmessage.BitmessageContext; 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; /** * Provides singleton objects across the application. */ public class Singleton { - private static BitmessageContext bitmessageContext; + private static SqlHelper sqlHelper; + private static Security security; + private static MessageRepository messageRepository; private static MessageListener messageListener; + private static AddressRepository addressRepository; - public static BitmessageContext getBitmessageContext(Context context) { - if (bitmessageContext == null) { - synchronized (Singleton.class) { - if (bitmessageContext == null) { - final Context ctx = context.getApplicationContext(); - SqlHelper sqlHelper = new SqlHelper(ctx); - bitmessageContext = new BitmessageContext.Builder() - .security(new SpongySecurity()) - .nodeRegistry(new MemoryNodeRegistry()) - .inventory(new AndroidInventory(sqlHelper)) - .addressRepo(new AndroidAddressRepository(sqlHelper)) - .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) - .networkHandler(new DefaultNetworkHandler()) - .listener(getMessageListener(ctx)) - .build(); - } - } - } - return bitmessageContext; + static { + ch.dissem.bitmessage.utils.Singleton.initialize(new SpongySecurity()); } public static MessageListener getMessageListener(Context ctx) { @@ -50,4 +40,41 @@ public class Singleton { } return messageListener; } + + public static SqlHelper getSqlHelper(Context ctx) { + if (sqlHelper == null) { + synchronized (Singleton.class) { + if (sqlHelper == null) { + sqlHelper = new SqlHelper(ctx.getApplicationContext()); + } + } + } + return sqlHelper; + } + + public static MessageRepository getMessageRepository(Context ctx) { + if (messageRepository == null) { + ctx = ctx.getApplicationContext(); + getSqlHelper(ctx); + synchronized (Singleton.class) { + if (messageRepository == null) { + messageRepository = new AndroidMessageRepository(sqlHelper, ctx); + } + } + } + return messageRepository; + } + + public static AddressRepository getAddressRepository(Context ctx) { + if (addressRepository == null) { + ctx = ctx.getApplicationContext(); + getSqlHelper(ctx); + synchronized (Singleton.class) { + if (addressRepository == null) { + addressRepository = new AndroidAddressRepository(sqlHelper); + } + } + } + return addressRepository; + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java new file mode 100644 index 0000000..76176fb --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java @@ -0,0 +1,186 @@ +package ch.dissem.apps.abit.synchronization; + +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.net.InetAddress; +import java.net.UnknownHostException; + +import ch.dissem.apps.abit.listener.MessageListener; +import ch.dissem.apps.abit.notification.NetworkNotification; +import ch.dissem.apps.abit.repository.AndroidInventory; +import ch.dissem.apps.abit.repository.SqlHelper; +import ch.dissem.apps.abit.service.Singleton; +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; +import ch.dissem.bitmessage.ports.MemoryNodeRegistry; +import ch.dissem.bitmessage.security.sc.SpongySecurity; + +import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; + +/** + * Define a Service that returns an IBinder for the + * sync adapter class, allowing the sync adapter framework to call + * onPerformSync(). + */ +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; + public static final int MSG_SUBSCRIBE_AND_ADD_CONTACT = 23; + public static final int MSG_START_NODE = 100; + public static final int MSG_STOP_NODE = 101; + + public static final String DATA_FIELD_ADDRESS = "address"; + + // Object to use as a thread-safe lock + private static final Object lock = new Object(); + + private static MessageListener messageListener = null; + private static NetworkNotification notification = null; + private static BitmessageContext bmc = null; + + private static volatile boolean running = false; + + private static Messenger messenger; + + public static boolean isRunning() { + return running; + } + + @Override + public void onCreate() { + synchronized (lock) { + if (bmc == null) { + messageListener = Singleton.getMessageListener(this); + SqlHelper sqlHelper = Singleton.getSqlHelper(this); + bmc = new BitmessageContext.Builder() + .security(new SpongySecurity()) + .nodeRegistry(new MemoryNodeRegistry()) + .inventory(new AndroidInventory(sqlHelper)) + .addressRepo(Singleton.getAddressRepository(this)) + .messageRepo(Singleton.getMessageRepository(this)) + .networkHandler(new DefaultNetworkHandler()) + .listener(messageListener) + .build(); + notification = new NetworkNotification(this, bmc); + messenger = new Messenger(new IncomingHandler()); + } + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return Service.START_STICKY; + } + + @Override + public void onDestroy() { + bmc.shutdown(); + running = false; + } + + /** + * Return an object that allows the system to invoke + * the sync adapter. + */ + @Override + public IBinder onBind(Intent intent) { + return messenger.getBinder(); + } + + private class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_CREATE_IDENTITY: + BitmessageAddress identity = bmc.createIdentity(false); + if (msg.replyTo != null) { + try { + Message message = Message.obtain(this, MSG_CREATE_IDENTITY); + Bundle bundle = new Bundle(); + bundle.putSerializable(DATA_FIELD_ADDRESS, identity); + message.setData(bundle); + msg.replyTo.send(message); + } catch (RemoteException e) { + LOG.debug(e.getMessage(), e); + } + } + break; + case MSG_SUBSCRIBE: + BitmessageAddress address = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); + bmc.addSubscribtion(address); + 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_START_NODE: + startService(new Intent(BitmessageService.this, BitmessageService.class)); + // TODO: warn user, option to restrict to WiFi + running = true; + 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)); + break; + default: + super.handleMessage(msg); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java index f744299..dca34dc 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -1,16 +1,12 @@ package ch.dissem.apps.abit.synchronization; import android.app.Service; -import android.content.Context; import android.content.Intent; import android.os.IBinder; -import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.notification.NetworkNotification; -import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidInventory; -import ch.dissem.apps.abit.repository.AndroidMessageRepository; import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; @@ -26,20 +22,12 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF * onPerformSync(). */ public class SyncService extends Service { - private static MessageListener messageListener = null; - private static BitmessageContext bmc = null; // Storage for an instance of the sync adapter private static SyncAdapter syncAdapter = null; // Object to use as a thread-safe lock private static final Object syncAdapterLock = new Object(); - private static volatile boolean running = false; - - public static boolean isRunning() { - return running; - } - - /* + /** * Instantiate the sync adapter object. */ @Override @@ -50,46 +38,12 @@ public class SyncService extends Service { * Disallow parallel syncs */ synchronized (syncAdapterLock) { - final Context ctx = getApplicationContext(); - if (bmc == null) { -// messageListener = new MessageListener(ctx); -// SqlHelper sqlHelper = new SqlHelper(ctx); -// bmc = new BitmessageContext.Builder() -// .security(new SpongySecurity()) -// .nodeRegistry(new MemoryNodeRegistry()) -// .inventory(new AndroidInventory(sqlHelper)) -// .addressRepo(new AndroidAddressRepository(sqlHelper)) -// .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) -// .networkHandler(new DefaultNetworkHandler()) -// .listener(messageListener) -// .build(); - // FIXME: this needs to change once I figured out how to get rid of those singletons - messageListener = Singleton.getMessageListener(ctx); - bmc = Singleton.getBitmessageContext(ctx); - } if (syncAdapter == null) { - syncAdapter = new SyncAdapter(ctx, bmc); + syncAdapter = new SyncAdapter(this, null); // FIXME } } } - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - // TODO: warn user, option to restrict to WiFi - running = true; - NetworkNotification networkNotification = new NetworkNotification(this); - startForeground(ONGOING_NOTIFICATION_ID, networkNotification.getNotification()); - bmc.startup(); - networkNotification.show(); - return Service.START_STICKY; - } - - @Override - public void onDestroy() { - bmc.shutdown(); - running = false; - } - /** * Return an object that allows the system to invoke * the sync adapter.