Moving Bitmessage context into a foreground service (work in progress)

This commit is contained in:
Christian Basler 2015-10-23 22:40:09 +02:00
parent 9b1bf6bdb3
commit f5bf5c8bca
14 changed files with 441 additions and 143 deletions

View File

@ -102,6 +102,7 @@
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
</intent-filter> </intent-filter>
</activity> </activity>
<service android:name=".synchronization.BitmessageService" />
<!-- Synchronization --> <!-- Synchronization -->
<provider <provider

View File

@ -17,14 +17,13 @@
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.ListFragment; import android.support.v4.app.ListFragment;
import android.view.View; import android.view.View;
import android.widget.ListView; import android.widget.ListView;
import ch.dissem.apps.abit.listener.ListSelectionListener; import ch.dissem.apps.abit.listener.ListSelectionListener;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
/** /**
@ -45,7 +44,6 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
public void onItemSelected(Object plaintext) { public void onItemSelected(Object plaintext) {
} }
}; };
protected BitmessageContext bmc;
/** /**
* The fragment's current callback object, which is notified of list item * The fragment's current callback object, which is notified of list item
* clicks. * clicks.
@ -59,13 +57,6 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
abstract void updateList(Label label); abstract void updateList(Label label);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bmc = Singleton.getBitmessageContext(getActivity());
}
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@ -89,15 +80,15 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context context) {
super.onAttach(activity); super.onAttach(context);
// Activities containing this fragment must implement its callbacks. // 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."); throw new IllegalStateException("Activity must implement fragment's callbacks.");
} }
callbacks = (ListSelectionListener) activity; callbacks = (ListSelectionListener) context;
} }
@Override @Override

View File

@ -6,12 +6,15 @@ import android.support.v4.app.Fragment;
import android.view.*; import android.view.*;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.apps.abit.util.Drawables; import ch.dissem.apps.abit.util.Drawables;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.MessageRepository;
import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import java.util.Iterator; import java.util.Iterator;
@ -84,7 +87,7 @@ public class MessageDetailFragment extends Fragment {
} }
} }
if (removed) { if (removed) {
Singleton.getBitmessageContext(inflater.getContext()).messages().save(item); Singleton.getMessageRepository(inflater.getContext()).save(item);
} }
return rootView; return rootView;
} }
@ -103,7 +106,7 @@ public class MessageDetailFragment extends Fragment {
@Override @Override
public boolean onOptionsItemSelected(MenuItem menuItem) { public boolean onOptionsItemSelected(MenuItem menuItem) {
BitmessageContext bmc = Singleton.getBitmessageContext(getActivity()); MessageRepository messageRepo = Singleton.getMessageRepository(getContext());
switch (menuItem.getItemId()) { switch (menuItem.getItemId()) {
case R.id.reply: case R.id.reply:
Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class);
@ -113,21 +116,21 @@ public class MessageDetailFragment extends Fragment {
return true; return true;
case R.id.delete: case R.id.delete:
if (isInTrash(item)) { if (isInTrash(item)) {
bmc.messages().remove(item); messageRepo.remove(item);
} else { } else {
item.getLabels().clear(); item.getLabels().clear();
item.addLabels(bmc.messages().getLabels(Label.Type.TRASH)); item.addLabels(messageRepo.getLabels(Label.Type.TRASH));
bmc.messages().save(item); messageRepo.save(item);
} }
getActivity().onBackPressed(); getActivity().onBackPressed();
return true; return true;
case R.id.mark_unread: case R.id.mark_unread:
item.addLabels(bmc.messages().getLabels(Label.Type.UNREAD)); item.addLabels(messageRepo.getLabels(Label.Type.UNREAD));
bmc.messages().save(item); messageRepo.save(item);
return true; return true;
case R.id.archive: case R.id.archive:
item.getLabels().clear(); item.getLabels().clear();
bmc.messages().save(item); messageRepo.save(item);
return true; return true;
default: default:
return false; return false;

View File

@ -2,9 +2,17 @@ package ch.dissem.apps.abit;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle; 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.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; 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.listener.ListSelectionListener;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.apps.abit.synchronization.Authenticator; import ch.dissem.apps.abit.synchronization.Authenticator;
import ch.dissem.apps.abit.synchronization.BitmessageService;
import ch.dissem.apps.abit.synchronization.SyncService; import ch.dissem.apps.abit.synchronization.SyncService;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.Label; 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; import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY;
@ -79,15 +92,36 @@ public class MessageListActivity extends AppCompatActivity
*/ */
private boolean twoPane; 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 AccountHeader accountHeader;
private BitmessageContext bmc;
private Label selectedLabel; private Label selectedLabel;
private MessageRepository messageRepo;
private AddressRepository addressRepo;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
bmc = Singleton.getBitmessageContext(this); messageRepo = Singleton.getMessageRepository(this);
selectedLabel = bmc.messages().getLabels().get(0); addressRepo = Singleton.getAddressRepository(this);
selectedLabel = messageRepo.getLabels().get(0);
setContentView(R.layout.activity_message_list); setContentView(R.layout.activity_message_list);
@ -152,7 +186,7 @@ public class MessageListActivity extends AppCompatActivity
private void createDrawer(Toolbar toolbar) { private void createDrawer(Toolbar toolbar) {
final ArrayList<IProfile> profiles = new ArrayList<>(); final ArrayList<IProfile> profiles = new ArrayList<>();
for (BitmessageAddress identity : bmc.addresses().getIdentities()) { for (BitmessageAddress identity : addressRepo.getIdentities()) {
LOG.info("Adding identity " + identity.getAddress()); LOG.info("Adding identity " + identity.getAddress());
profiles.add(new ProfileDrawerItem() profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity)) .withIcon(new Identicon(identity))
@ -183,16 +217,12 @@ public class MessageListActivity extends AppCompatActivity
@Override @Override
public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) { public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
if (profile.getIdentifier() == ADD_IDENTITY) { if (profile.getIdentifier() == ADD_IDENTITY) {
BitmessageAddress identity = bmc.createIdentity(false); try {
IProfile newProfile = new ProfileDrawerItem() Message message = Message.obtain(null, BitmessageService.MSG_CREATE_IDENTITY);
.withName(identity.toString()) message.replyTo = messenger;
.withEmail(identity.getAddress()) service.send(message);
.withTag(identity); } catch (RemoteException e) {
if (accountHeader.getProfiles() != null) { LOG.error(e.getMessage(), e);
//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);
} }
} }
// false if it should close the drawer // false if it should close the drawer
@ -202,7 +232,7 @@ public class MessageListActivity extends AppCompatActivity
.build(); .build();
ArrayList<IDrawerItem> drawerItems = new ArrayList<>(); ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
for (Label label : bmc.messages().getLabels()) { for (Label label : messageRepo.getLabels()) {
PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag(label); PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag(label);
switch (label.getType()) { switch (label.getType()) {
case INBOX: case INBOX:
@ -254,13 +284,21 @@ public class MessageListActivity extends AppCompatActivity
@Override @Override
public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
if (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 { } 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() { .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
@Override @Override
@ -303,7 +341,7 @@ public class MessageListActivity extends AppCompatActivity
((MessageListFragment) getSupportFragmentManager() ((MessageListFragment) getSupportFragmentManager()
.findFragmentById(R.id.item_list)).updateList(selectedLabel); .findFragmentById(R.id.item_list)).updateList(selectedLabel);
} else { } else {
MessageListFragment listFragment = new MessageListFragment(getApplicationContext()); MessageListFragment listFragment = new MessageListFragment();
changeList(listFragment); changeList(listFragment);
listFragment.updateList(selectedLabel); listFragment.updateList(selectedLabel);
} }
@ -360,4 +398,41 @@ public class MessageListActivity extends AppCompatActivity
return selectedLabel; 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);
}
}
}
} }

View File

@ -44,11 +44,6 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> {
public MessageListFragment() { public MessageListFragment() {
} }
@SuppressLint("ValidFragment")
public MessageListFragment(Context ctx) {
bmc = Singleton.getBitmessageContext(ctx);
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -73,7 +68,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> {
getActivity(), getActivity(),
android.R.layout.simple_list_item_activated_1, android.R.layout.simple_list_item_activated_1,
android.R.id.text1, android.R.id.text1,
bmc.messages().findMessages(label)) { Singleton.getMessageRepository(getContext()).findMessages(label)) {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
@ -138,7 +133,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> {
case R.id.empty_trash: case R.id.empty_trash:
if (currentLabel.getType() != Label.Type.TRASH) return true; if (currentLabel.getType() != Label.Type.TRASH) return true;
MessageRepository repo = bmc.messages(); MessageRepository repo = Singleton.getMessageRepository(getContext());
for (Plaintext message : repo.findMessages(currentLabel)) { for (Plaintext message : repo.findMessages(currentLabel)) {
repo.remove(message); repo.remove(message);
} }

View File

@ -17,19 +17,52 @@
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.app.Activity; 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.net.Uri;
import android.os.Bundle; 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.support.v7.app.AppCompatActivity;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; 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 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 { 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -70,14 +103,29 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
ok.setOnClickListener(new View.OnClickListener() { ok.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
BitmessageContext bmc = Singleton.getBitmessageContext(OpenBitmessageLinkActivity.this);
BitmessageAddress bmAddress = new BitmessageAddress(address); BitmessageAddress bmAddress = new BitmessageAddress(address);
bmAddress.setAlias(label.getText().toString()); bmAddress.setAlias(label.getText().toString());
if (subscribe.isChecked()) {
bmc.addSubscribtion(bmAddress); final int what;
} if (subscribe.isChecked() && importContact.isChecked())
if (importContact.isChecked()) { what = MSG_SUBSCRIBE_AND_ADD_CONTACT;
bmc.addContact(bmAddress); 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); setResult(Activity.RESULT_OK);
finish(); finish();
@ -110,4 +158,19 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
return new String[0]; 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();
}
} }

View File

@ -27,6 +27,7 @@ import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
@ -115,7 +116,7 @@ public class SubscriptionDetailFragment extends Fragment {
@Override @Override
public void onPause() { public void onPause() {
Singleton.getBitmessageContext(getActivity()).addresses().save(item); Singleton.getAddressRepository(getContext()).save(item);
super.onPause(); super.onPause();
} }
} }

View File

@ -24,6 +24,8 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
@ -32,7 +34,7 @@ import java.util.Comparator;
import java.util.List; 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> { public class SubscriptionListFragment extends AbstractItemListFragment<BitmessageAddress> {
@Override @Override
@ -43,7 +45,7 @@ public class SubscriptionListFragment extends AbstractItemListFragment<Bitmessag
} }
public void updateList() { public void updateList() {
List<BitmessageAddress> addresses = bmc.addresses().getContacts(); List<BitmessageAddress> addresses = Singleton.getAddressRepository(getContext()).getContacts();
Collections.sort(addresses, new Comparator<BitmessageAddress>() { Collections.sort(addresses, new Comparator<BitmessageAddress>() {
/** /**
* Yields the following order: * Yields the following order:

View File

@ -25,9 +25,9 @@ public class NetworkNotification extends AbstractNotification {
private final BitmessageContext bmc; private final BitmessageContext bmc;
private NotificationCompat.Builder builder; private NotificationCompat.Builder builder;
public NetworkNotification(Context ctx) { public NetworkNotification(Context ctx, BitmessageContext bmc) {
super(ctx); super(ctx.getApplicationContext());
bmc = Singleton.getBitmessageContext(ctx); this.bmc = bmc;
builder = new NotificationCompat.Builder(ctx); builder = new NotificationCompat.Builder(ctx);
builder.setSmallIcon(R.drawable.ic_notification_full_node) builder.setSmallIcon(R.drawable.ic_notification_full_node)
.setContentTitle(ctx.getString(R.string.bitmessage_full_node)) .setContentTitle(ctx.getString(R.string.bitmessage_full_node))

View File

@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.Plaintext;
import static ch.dissem.apps.abit.util.Drawables.toBitmap; import static ch.dissem.apps.abit.util.Drawables.toBitmap;
public class NewMessageNotification extends AbstractNotification { public class NewMessageNotification extends AbstractNotification {
public static final int NEW_MESSAGE_NOTIFICATION_ID = 1;
private static final StyleSpan SPAN_EMPHASIS = new StyleSpan(Typeface.BOLD); private static final StyleSpan SPAN_EMPHASIS = new StyleSpan(Typeface.BOLD);
public NewMessageNotification(Context ctx) { public NewMessageNotification(Context ctx) {
@ -76,6 +77,6 @@ public class NewMessageNotification extends AbstractNotification {
@Override @Override
protected int getNotificationId() { protected int getNotificationId() {
return 1; return NEW_MESSAGE_NOTIFICATION_ID;
} }
} }

View File

@ -23,11 +23,13 @@ import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import ch.dissem.apps.abit.R; import ch.dissem.apps.abit.R;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.InternalContext;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.utils.Encode; 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. * {@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 Logger LOG = LoggerFactory.getLogger(AndroidMessageRepository.class);
private static final String TABLE_NAME = "Message"; 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 static final String LBL_COLUMN_ORDER = "ord";
private final SqlHelper sql; private final SqlHelper sql;
private final Context ctx; private final Context ctx;
private InternalContext bmc;
private final AddressRepository addressRepo;
public AndroidMessageRepository(SqlHelper sql, Context ctx) { public AndroidMessageRepository(SqlHelper sql, Context ctx) {
this.sql = sql; this.sql = sql;
this.ctx = ctx; this.ctx = ctx;
} this.addressRepo = Singleton.getAddressRepository(ctx);
@Override
public void setContext(InternalContext context) {
bmc = context;
} }
@Override @Override
@ -230,8 +229,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
long id = c.getLong(c.getColumnIndex(COLUMN_ID)); long id = c.getLong(c.getColumnIndex(COLUMN_ID));
builder.id(id); builder.id(id);
builder.IV(new InventoryVector(iv)); builder.IV(new InventoryVector(iv));
builder.from(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); builder.from(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER))));
builder.to(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); builder.to(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT))));
builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT))); builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT)));
builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED))); builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED)));
builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS)))); 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 // save from address if necessary
if (message.getId() == null) { 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.getPrivateKey() == null) {
if (savedAddress != null && savedAddress.getAlias() != null) { if (savedAddress != null && savedAddress.getAlias() != null) {
message.getFrom().setAlias(savedAddress.getAlias()); message.getFrom().setAlias(savedAddress.getAlias());
} }
bmc.getAddressRepo().save(message.getFrom()); addressRepo.save(message.getFrom());
} }
} }

View File

@ -2,6 +2,7 @@ package ch.dissem.apps.abit.service;
import android.content.Context; import android.content.Context;
import ch.dissem.apps.abit.MessageListActivity;
import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.listener.MessageListener;
import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidAddressRepository;
import ch.dissem.apps.abit.repository.AndroidInventory; 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.apps.abit.repository.SqlHelper;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry; 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; import ch.dissem.bitmessage.security.sc.SpongySecurity;
/** /**
* Provides singleton objects across the application. * Provides singleton objects across the application.
*/ */
public class Singleton { 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 MessageListener messageListener;
private static AddressRepository addressRepository;
public static BitmessageContext getBitmessageContext(Context context) { static {
if (bitmessageContext == null) { ch.dissem.bitmessage.utils.Singleton.initialize(new SpongySecurity());
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;
} }
public static MessageListener getMessageListener(Context ctx) { public static MessageListener getMessageListener(Context ctx) {
@ -50,4 +40,41 @@ public class Singleton {
} }
return messageListener; 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;
}
} }

View File

@ -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);
}
}
}
}

View File

@ -1,16 +1,12 @@
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.app.Service; import android.app.Service;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import ch.dissem.apps.abit.MessageListActivity;
import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.listener.MessageListener;
import ch.dissem.apps.abit.notification.NetworkNotification; 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.AndroidInventory;
import ch.dissem.apps.abit.repository.AndroidMessageRepository;
import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.apps.abit.repository.SqlHelper;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
@ -26,20 +22,12 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF
* onPerformSync(). * onPerformSync().
*/ */
public class SyncService extends Service { public class SyncService extends Service {
private static MessageListener messageListener = null;
private static BitmessageContext bmc = null;
// Storage for an instance of the sync adapter // Storage for an instance of the sync adapter
private static SyncAdapter syncAdapter = null; private static SyncAdapter syncAdapter = null;
// Object to use as a thread-safe lock // Object to use as a thread-safe lock
private static final Object syncAdapterLock = new Object(); private static final Object syncAdapterLock = new Object();
private static volatile boolean running = false; /**
public static boolean isRunning() {
return running;
}
/*
* Instantiate the sync adapter object. * Instantiate the sync adapter object.
*/ */
@Override @Override
@ -50,46 +38,12 @@ public class SyncService extends Service {
* Disallow parallel syncs * Disallow parallel syncs
*/ */
synchronized (syncAdapterLock) { 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) { 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 * Return an object that allows the system to invoke
* the sync adapter. * the sync adapter.