From 51bcf7b1b8abaa396848a36d806dc99a8065c968 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 18 Oct 2016 14:14:15 +0100
Subject: [PATCH] Don't use messaging group ID as proxy for contact ID.

---
 .../AndroidNotificationManagerImpl.java       | 96 ++++++++++---------
 .../api/AndroidNotificationManager.java       |  7 +-
 .../android/contact/ContactItem.java          |  1 +
 .../android/contact/ContactListFragment.java  | 11 +--
 .../android/contact/ContactListItem.java      |  9 +-
 .../contact/ContactListItemViewHolder.java    | 19 ++--
 .../android/contact/ConversationActivity.java | 96 ++++++++++---------
 .../introduction/ContactChooserFragment.java  |  9 +-
 .../creation/BaseGroupInviteActivity.java     |  2 -
 .../sharing/ContactSelectorActivity.java      |  7 --
 .../sharing/ContactSelectorListener.java      |  2 +-
 .../sharing/SelectableContactItem.java        | 10 +-
 .../android/util/AndroidUtils.java            |  8 ++
 .../api/messaging/ConversationManager.java    |  4 -
 .../messaging/ConversationManagerImpl.java    | 26 +----
 15 files changed, 139 insertions(+), 168 deletions(-)

diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
index 7d47079e13..973dc01307 100644
--- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
@@ -34,7 +34,6 @@ import org.briarproject.api.event.PrivateMessageReceivedEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
 import org.briarproject.api.lifecycle.Service;
 import org.briarproject.api.lifecycle.ServiceException;
-import org.briarproject.api.messaging.MessagingManager;
 import org.briarproject.api.settings.Settings;
 import org.briarproject.api.settings.SettingsManager;
 import org.briarproject.api.sync.GroupId;
@@ -65,6 +64,7 @@ import static org.briarproject.android.BriarActivity.GROUP_ID;
 import static org.briarproject.android.NavDrawerActivity.INTENT_BLOGS;
 import static org.briarproject.android.NavDrawerActivity.INTENT_CONTACTS;
 import static org.briarproject.android.NavDrawerActivity.INTENT_FORUMS;
+import static org.briarproject.android.contact.ConversationActivity.CONTACT_ID;
 import static org.briarproject.android.fragment.SettingsFragment.PREF_NOTIFY_BLOG;
 import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
 
@@ -100,19 +100,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 
 	private final Executor dbExecutor;
 	private final SettingsManager settingsManager;
-	private final MessagingManager messagingManager;
 	private final AndroidExecutor androidExecutor;
 	private final Context appContext;
 	private final BroadcastReceiver receiver = new DeleteIntentReceiver();
 	private final AtomicBoolean used = new AtomicBoolean(false);
 
 	// The following must only be accessed on the main UI thread
-	private final Map<GroupId, Integer> contactCounts = new HashMap<>();
+	private final Map<ContactId, Integer> contactCounts = new HashMap<>();
 	private final Map<GroupId, Integer> forumCounts = new HashMap<>();
 	private final Map<GroupId, Integer> blogCounts = new HashMap<>();
 	private int contactTotal = 0, forumTotal = 0, blogTotal = 0;
 	private int introductionTotal = 0;
 	private int nextRequestId = 0;
+	private ContactId blockedContact = null;
 	private GroupId blockedGroup = null;
 	private boolean blockContacts = false, blockForums = false;
 	private boolean blockBlogs = false, blockIntroductions = false;
@@ -121,11 +121,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 
 	@Inject
 	AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
-			SettingsManager settingsManager, MessagingManager messagingManager,
-			AndroidExecutor androidExecutor, Application app) {
+			SettingsManager settingsManager, AndroidExecutor androidExecutor,
+			Application app) {
 		this.dbExecutor = dbExecutor;
 		this.settingsManager = settingsManager;
-		this.messagingManager = messagingManager;
 		this.androidExecutor = androidExecutor;
 		appContext = app.getApplicationContext();
 	}
@@ -165,7 +164,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
 			@Override
 			public Void call() {
-				clearPrivateMessageNotification();
+				clearContactNotification();
 				clearForumPostNotification();
 				clearBlogPostNotification();
 				clearIntroductionSuccessNotification();
@@ -181,7 +180,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	}
 
 	@UiThread
-	private void clearPrivateMessageNotification() {
+	private void clearContactNotification() {
 		contactCounts.clear();
 		contactTotal = 0;
 		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -222,7 +221,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
 		} else if (e instanceof PrivateMessageReceivedEvent) {
 			PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
-			showPrivateMessageNotification(p.getGroupId());
+			showContactNotification(p.getContactId());
 		} else if (e instanceof ForumPostReceivedEvent) {
 			ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
 			showForumPostNotification(f.getGroupId());
@@ -231,16 +230,17 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			showBlogPostNotification(b.getGroupId());
 		} else if (e instanceof IntroductionRequestReceivedEvent) {
 			ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
-			showNotificationForPrivateConversation(c);
+			showContactNotification(c);
 		} else if (e instanceof IntroductionResponseReceivedEvent) {
-			ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId();
-			showNotificationForPrivateConversation(c);
+			ContactId c =
+					((IntroductionResponseReceivedEvent) e).getContactId();
+			showContactNotification(c);
 		} else if (e instanceof InvitationRequestReceivedEvent) {
 			ContactId c = ((InvitationRequestReceivedEvent) e).getContactId();
-			showNotificationForPrivateConversation(c);
+			showContactNotification(c);
 		} else if (e instanceof InvitationResponseReceivedEvent) {
 			ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();
-			showNotificationForPrivateConversation(c);
+			showContactNotification(c);
 		} else if (e instanceof IntroductionSucceededEvent) {
 			showIntroductionNotification();
 		}
@@ -260,38 +260,38 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		});
 	}
 
-	private void showPrivateMessageNotification(final GroupId g) {
+	private void showContactNotification(final ContactId c) {
 		androidExecutor.runOnUiThread(new Runnable() {
 			@Override
 			public void run() {
 				if (blockContacts) return;
-				if (g.equals(blockedGroup)) return;
-				Integer count = contactCounts.get(g);
-				if (count == null) contactCounts.put(g, 1);
-				else contactCounts.put(g, count + 1);
+				if (c.equals(blockedContact)) return;
+				Integer count = contactCounts.get(c);
+				if (count == null) contactCounts.put(c, 1);
+				else contactCounts.put(c, count + 1);
 				contactTotal++;
-				updatePrivateMessageNotification();
+				updateContactNotification();
 			}
 		});
 	}
 
 	@Override
-	public void clearPrivateMessageNotification(final GroupId g) {
+	public void clearContactNotification(final ContactId c) {
 		androidExecutor.runOnUiThread(new Runnable() {
 			@Override
 			public void run() {
-				Integer count = contactCounts.remove(g);
+				Integer count = contactCounts.remove(c);
 				if (count == null) return; // Already cleared
 				contactTotal -= count;
-				updatePrivateMessageNotification();
+				updateContactNotification();
 			}
 		});
 	}
 
 	@UiThread
-	private void updatePrivateMessageNotification() {
+	private void updateContactNotification() {
 		if (contactTotal == 0) {
-			clearPrivateMessageNotification();
+			clearContactNotification();
 		} else if (settings.getBoolean("notifyPrivateMessages", true)) {
 			NotificationCompat.Builder b =
 					new NotificationCompat.Builder(appContext);
@@ -315,10 +315,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			if (contactCounts.size() == 1) {
 				// Touching the notification shows the relevant conversation
 				Intent i = new Intent(appContext, ConversationActivity.class);
-				GroupId g = contactCounts.keySet().iterator().next();
-				i.putExtra(GROUP_ID, g.getBytes());
-				String idHex = StringUtils.toHexString(g.getBytes());
-				i.setData(Uri.parse(CONTACT_URI + "/" + idHex));
+				ContactId c = contactCounts.keySet().iterator().next();
+				i.putExtra(CONTACT_ID, c.getInt());
+				i.setData(Uri.parse(CONTACT_URI + "/" + c.getInt()));
 				i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
 				TaskStackBuilder t = TaskStackBuilder.create(appContext);
 				t.addParentStack(ConversationActivity.class);
@@ -362,7 +361,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		androidExecutor.runOnUiThread(new Runnable() {
 			@Override
 			public void run() {
-				clearPrivateMessageNotification();
+				clearContactNotification();
 				clearIntroductionSuccessNotification();
 			}
 		});
@@ -613,6 +612,26 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		});
 	}
 
+	@Override
+	public void blockContactNotification(final ContactId c) {
+		androidExecutor.runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				blockedContact = c;
+			}
+		});
+	}
+
+	@Override
+	public void unblockContactNotification(final ContactId c) {
+		androidExecutor.runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				if (c.equals(blockedContact)) blockedContact = null;
+			}
+		});
+	}
+
 	@Override
 	public void blockAllContactNotifications() {
 		androidExecutor.runOnUiThread(new Runnable() {
@@ -675,21 +694,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		});
 	}
 
-	private void showNotificationForPrivateConversation(final ContactId c) {
-		dbExecutor.execute(new Runnable() {
-			@Override
-			public void run() {
-				try {
-					GroupId g = messagingManager.getConversationId(c);
-					showPrivateMessageNotification(g);
-				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-				}
-			}
-		});
-	}
-
 	private class DeleteIntentReceiver extends BroadcastReceiver {
 
 		@Override
@@ -699,7 +703,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				@Override
 				public void run() {
 					if (CLEAR_PRIVATE_MESSAGE_ACTION.equals(action)) {
-						clearPrivateMessageNotification();
+						clearContactNotification();
 					} else if (CLEAR_FORUM_ACTION.equals(action)) {
 						clearForumPostNotification();
 					} else if (CLEAR_BLOG_ACTION.equals(action)) {
diff --git a/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java b/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
index 217d8937d6..1140ff5730 100644
--- a/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
+++ b/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
@@ -1,5 +1,6 @@
 package org.briarproject.android.api;
 
+import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.sync.GroupId;
 
 /**
@@ -8,7 +9,7 @@ import org.briarproject.api.sync.GroupId;
  */
 public interface AndroidNotificationManager {
 
-	void clearPrivateMessageNotification(GroupId g);
+	void clearContactNotification(ContactId c);
 
 	void clearAllContactNotifications();
 
@@ -20,6 +21,10 @@ public interface AndroidNotificationManager {
 
 	void clearAllBlogPostNotifications();
 
+	void blockContactNotification(ContactId c);
+
+	void unblockContactNotification(ContactId c);
+
 	void blockNotification(GroupId g);
 
 	void unblockNotification(GroupId g);
diff --git a/briar-android/src/org/briarproject/android/contact/ContactItem.java b/briar-android/src/org/briarproject/android/contact/ContactItem.java
index 9e92c8983a..c5e5764a75 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactItem.java
@@ -10,6 +10,7 @@ import javax.annotation.concurrent.NotThreadSafe;
 public class ContactItem {
 
 	private final Contact contact;
+
 	private boolean connected;
 
 	public ContactItem(Contact contact, boolean connected) {
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index 2d2a098b37..bd8f9467af 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -48,7 +48,6 @@ import org.briarproject.api.messaging.PrivateMessageHeader;
 import org.briarproject.api.plugins.ConnectionRegistry;
 import org.briarproject.api.sharing.InvitationRequest;
 import org.briarproject.api.sharing.InvitationResponse;
-import org.briarproject.api.sync.GroupId;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -60,7 +59,7 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
 import static android.support.v4.view.ViewCompat.getTransitionName;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.contact.ConversationActivity.CONTACT_ID;
 
 public class ContactListFragment extends BaseFragment implements EventListener {
 
@@ -111,10 +110,10 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 				new OnContactClickListener<ContactListItem>() {
 					@Override
 					public void onItemClick(View view, ContactListItem item) {
-						GroupId groupId = item.getGroupId();
 						Intent i = new Intent(getActivity(),
 								ConversationActivity.class);
-						i.putExtra(GROUP_ID, groupId.getBytes());
+						ContactId contactId = item.getContact().getId();
+						i.putExtra(CONTACT_ID, contactId.getInt());
 
 						// work-around for android bug #224270
 						if (Build.VERSION.SDK_INT >= 23) {
@@ -200,14 +199,12 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 					for (Contact c : contactManager.getActiveContacts()) {
 						try {
 							ContactId id = c.getId();
-							GroupId groupId =
-									conversationManager.getConversationId(id);
 							GroupCount count =
 									conversationManager.getGroupCount(id);
 							boolean connected =
 									connectionRegistry.isConnected(c.getId());
 							contacts.add(new ContactListItem(c, connected,
-									groupId, count));
+									count));
 						} catch (NoSuchContactException e) {
 							// Continue
 						}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListItem.java b/briar-android/src/org/briarproject/android/contact/ContactListItem.java
index 19808d982b..73f49047ad 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListItem.java
@@ -3,7 +3,6 @@ package org.briarproject.android.contact;
 import org.briarproject.api.clients.MessageTracker.GroupCount;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.nullsafety.NotNullByDefault;
-import org.briarproject.api.sync.GroupId;
 
 import javax.annotation.concurrent.NotThreadSafe;
 
@@ -11,15 +10,13 @@ import javax.annotation.concurrent.NotThreadSafe;
 @NotNullByDefault
 public class ContactListItem extends ContactItem {
 
-	private final GroupId groupId;
 	private boolean empty;
 	private long timestamp;
 	private int unread;
 
-	public ContactListItem(Contact contact,	boolean connected, GroupId groupId,
+	public ContactListItem(Contact contact, boolean connected,
 			GroupCount count) {
 		super(contact, connected);
-		this.groupId = groupId;
 		this.empty = count.getMsgCount() == 0;
 		this.unread = count.getUnreadCount();
 		this.timestamp = count.getLatestMsgTime();
@@ -32,10 +29,6 @@ public class ContactListItem extends ContactItem {
 			unread++;
 	}
 
-	GroupId getGroupId() {
-		return groupId;
-	}
-
 	boolean isEmpty() {
 		return empty;
 	}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListItemViewHolder.java b/briar-android/src/org/briarproject/android/contact/ContactListItemViewHolder.java
index 6e46526182..acbd27d82d 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListItemViewHolder.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListItemViewHolder.java
@@ -7,13 +7,13 @@ import android.widget.TextView;
 
 import org.briarproject.R;
 import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
+import org.briarproject.android.util.AndroidUtils;
+import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.nullsafety.NotNullByDefault;
-import org.briarproject.api.sync.GroupId;
 import org.jetbrains.annotations.Nullable;
 
 import static android.support.v4.view.ViewCompat.setTransitionName;
 import static org.briarproject.android.util.AndroidUtils.formatDate;
-import static org.briarproject.util.StringUtils.toHexString;
 
 @UiThread
 @NotNullByDefault
@@ -23,7 +23,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
 	private final TextView unread;
 	private final TextView date;
 
-	public ContactListItemViewHolder(View v) {
+	ContactListItemViewHolder(View v) {
 		super(v);
 		bulb = (ImageView) v.findViewById(R.id.bulbView);
 		unread = (TextView) v.findViewById(R.id.unreadCountView);
@@ -59,16 +59,9 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
 			bulb.setImageResource(R.drawable.contact_disconnected);
 		}
 
-		setTransitionName(avatar, getAvatarTransitionName(item.getGroupId()));
-		setTransitionName(bulb, getBulbTransitionName(item.getGroupId()));
-	}
-
-	private String getAvatarTransitionName(GroupId g) {
-		return "avatar" + toHexString(g.getBytes());
-	}
-
-	private String getBulbTransitionName(GroupId g) {
-		return "bulb" + toHexString(g.getBytes());
+		ContactId c = item.getContact().getId();
+		setTransitionName(avatar, AndroidUtils.getAvatarTransitionName(c));
+		setTransitionName(bulb, AndroidUtils.getBulbTransitionName(c));
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index 3659a691de..44de3c1626 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -30,6 +30,7 @@ import org.briarproject.android.BriarActivity;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.contact.ConversationAdapter.RequestListener;
 import org.briarproject.android.introduction.IntroductionActivity;
+import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.view.BriarRecyclerView;
 import org.briarproject.android.view.TextInputView;
 import org.briarproject.android.view.TextInputView.TextInputListener;
@@ -57,6 +58,7 @@ import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
 import org.briarproject.api.event.PrivateMessageReceivedEvent;
 import org.briarproject.api.forum.ForumSharingManager;
+import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.introduction.IntroductionManager;
 import org.briarproject.api.introduction.IntroductionMessage;
 import org.briarproject.api.introduction.IntroductionRequest;
@@ -73,6 +75,7 @@ import org.briarproject.api.sharing.InvitationMessage;
 import org.briarproject.api.sharing.InvitationRequest;
 import org.briarproject.api.sharing.InvitationResponse;
 import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.util.StringUtils;
 import org.jetbrains.annotations.NotNull;
@@ -106,6 +109,8 @@ import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESS
 public class ConversationActivity extends BriarActivity
 		implements EventListener, RequestListener, TextInputListener {
 
+	public static final String CONTACT_ID = "briar.CONTACT_ID";
+
 	private static final Logger LOG =
 			Logger.getLogger(ConversationActivity.class.getName());
 	private static final int REQUEST_CODE_INTRODUCTION = 1;
@@ -150,10 +155,10 @@ public class ConversationActivity extends BriarActivity
 	@Inject
 	volatile GroupInvitationManager groupInvitationManager;
 
-	private volatile GroupId groupId = null;
-	private volatile ContactId contactId = null;
-	private volatile String contactName = null;
-	private volatile byte[] contactIdenticonKey = null;
+	private volatile ContactId contactId;
+	private volatile String contactName;
+	private volatile AuthorId contactAuthorId;
+	private volatile GroupId messagingGroupId;
 
 	@SuppressWarnings("ConstantConditions")
 	@Override
@@ -161,9 +166,9 @@ public class ConversationActivity extends BriarActivity
 		super.onCreate(state);
 
 		Intent i = getIntent();
-		byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
-		if (b == null) throw new IllegalStateException();
-		groupId = new GroupId(b);
+		int id = i.getIntExtra(CONTACT_ID, -1);
+		if (id == -1) throw new IllegalStateException();
+		contactId = new ContactId(id);
 
 		setContentView(R.layout.activity_conversation);
 
@@ -185,9 +190,10 @@ public class ConversationActivity extends BriarActivity
 			ab.setDisplayShowTitleEnabled(false);
 		}
 
-		String hexGroupId = StringUtils.toHexString(b);
-		ViewCompat.setTransitionName(toolbarAvatar, "avatar" + hexGroupId);
-		ViewCompat.setTransitionName(toolbarStatus, "bulb" + hexGroupId);
+		ViewCompat.setTransitionName(toolbarAvatar,
+				AndroidUtils.getAvatarTransitionName(contactId));
+		ViewCompat.setTransitionName(toolbarStatus,
+				AndroidUtils.getBulbTransitionName(contactId));
 
 		adapter = new ConversationAdapter(this, this);
 		list = (BriarRecyclerView) findViewById(R.id.conversationView);
@@ -220,8 +226,8 @@ public class ConversationActivity extends BriarActivity
 	public void onStart() {
 		super.onStart();
 		eventBus.addListener(this);
-		notificationManager.blockNotification(groupId);
-		notificationManager.clearPrivateMessageNotification(groupId);
+		notificationManager.blockContactNotification(contactId);
+		notificationManager.clearContactNotification(contactId);
 		loadContactDetails();
 		loadMessages();
 		list.startPeriodicUpdate();
@@ -231,7 +237,7 @@ public class ConversationActivity extends BriarActivity
 	public void onStop() {
 		super.onStop();
 		eventBus.removeListener(this);
-		notificationManager.unblockNotification(groupId);
+		notificationManager.unblockContactNotification(contactId);
 		list.stopPeriodicUpdate();
 		if (isFinishing()) markMessagesRead();
 	}
@@ -287,13 +293,10 @@ public class ConversationActivity extends BriarActivity
 			public void run() {
 				try {
 					long now = System.currentTimeMillis();
-					if (contactId == null)
-						contactId = messagingManager.getContactId(groupId);
-					if (contactName == null || contactIdenticonKey == null) {
+					if (contactName == null || contactAuthorId == null) {
 						Contact contact = contactManager.getContact(contactId);
 						contactName = contact.getAuthor().getName();
-						contactIdenticonKey =
-								contact.getAuthor().getId().getBytes();
+						contactAuthorId = contact.getAuthor().getId();
 					}
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
@@ -314,7 +317,7 @@ public class ConversationActivity extends BriarActivity
 			@Override
 			public void run() {
 				toolbarAvatar.setImageDrawable(
-						new IdenticonDrawable(contactIdenticonKey));
+						new IdenticonDrawable(contactAuthorId.getBytes()));
 				toolbarTitle.setText(contactName);
 
 				if (connectionRegistry.isConnected(contactId)) {
@@ -341,8 +344,6 @@ public class ConversationActivity extends BriarActivity
 			public void run() {
 				try {
 					long now = System.currentTimeMillis();
-					if (contactId == null)
-						contactId = messagingManager.getContactId(groupId);
 					Collection<PrivateMessageHeader> headers =
 							messagingManager.getMessageHeaders(contactId);
 					Collection<IntroductionMessage> introductions =
@@ -419,12 +420,10 @@ public class ConversationActivity extends BriarActivity
 			ConversationItem item;
 			if (m instanceof IntroductionRequest) {
 				IntroductionRequest i = (IntroductionRequest) m;
-				item = ConversationItem
-						.from(ConversationActivity.this, contactName, i);
+				item = ConversationItem.from(this, contactName, i);
 			} else {
 				IntroductionResponse i = (IntroductionResponse) m;
-				item = ConversationItem
-						.from(ConversationActivity.this, contactName, i);
+				item = ConversationItem.from(this, contactName, i);
 			}
 			items.add(item);
 		}
@@ -432,12 +431,10 @@ public class ConversationActivity extends BriarActivity
 			ConversationItem item;
 			if (i instanceof InvitationRequest) {
 				InvitationRequest r = (InvitationRequest) i;
-				item = ConversationItem
-						.from(ConversationActivity.this, contactName, r);
+				item = ConversationItem.from(this, contactName, r);
 			} else {
 				InvitationResponse r = (InvitationResponse) i;
-				item = ConversationItem
-						.from(ConversationActivity.this, contactName, r);
+				item = ConversationItem.from(this, contactName, r);
 			}
 			items.add(item);
 		}
@@ -540,7 +537,7 @@ public class ConversationActivity extends BriarActivity
 			}
 		} else if (e instanceof PrivateMessageReceivedEvent) {
 			PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
-			if (p.getGroupId().equals(groupId)) {
+			if (p.getContactId().equals(contactId)) {
 				LOG.info("Message received, adding");
 				PrivateMessageHeader h = p.getMessageHeader();
 				addConversationItem(ConversationItem.from(h));
@@ -640,7 +637,8 @@ public class ConversationActivity extends BriarActivity
 		text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_BODY_LENGTH);
 		long timestamp = System.currentTimeMillis();
 		timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
-		createMessage(text, timestamp);
+		if (messagingGroupId == null) loadGroupId(text, timestamp);
+		else createMessage(text, timestamp);
 		textInputView.setText("");
 	}
 
@@ -650,13 +648,30 @@ public class ConversationActivity extends BriarActivity
 		return item == null ? 0 : item.getTime() + 1;
 	}
 
+	private void loadGroupId(final String body, final long timestamp) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					messagingGroupId =
+							messagingManager.getConversationId(contactId);
+					createMessage(body, timestamp);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+
+			}
+		});
+	}
+
 	private void createMessage(final String body, final long timestamp) {
 		cryptoExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
 				try {
 					storeMessage(privateMessageFactory.createPrivateMessage(
-							groupId, timestamp, body), body);
+							messagingGroupId, timestamp, body), body);
 				} catch (FormatException e) {
 					throw new RuntimeException(e);
 				}
@@ -674,14 +689,13 @@ public class ConversationActivity extends BriarActivity
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
 						LOG.info("Storing message took " + duration + " ms");
-					MessageId id = m.getMessage().getId();
-					PrivateMessageHeader h =
-							new PrivateMessageHeader(id, groupId,
-									m.getMessage().getTimestamp(), true, false,
-									false, false);
+					Message message = m.getMessage();
+					PrivateMessageHeader h = new PrivateMessageHeader(
+							message.getId(), message.getGroupId(),
+							message.getTimestamp(), true, false, false, false);
 					ConversationItem item = ConversationItem.from(h);
 					item.setBody(body);
-					bodyCache.put(id, body);
+					bodyCache.put(message.getId(), body);
 					addConversationItem(item);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
@@ -714,10 +728,6 @@ public class ConversationActivity extends BriarActivity
 			@Override
 			public void run() {
 				try {
-					// make sure contactId is initialised
-					if (contactId == null)
-						contactId = messagingManager.getContactId(groupId);
-					// remove contact with that ID
 					contactManager.removeContact(contactId);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
@@ -847,7 +857,7 @@ public class ConversationActivity extends BriarActivity
 					switch (item.getRequestType()) {
 						case INTRODUCTION:
 							respondToIntroductionRequest(item.getSessionId(),
-								accept, timestamp);
+									accept, timestamp);
 							break;
 						case FORUM:
 							respondToForumRequest(item.getSessionId(), accept);
diff --git a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
index 4e939d2b26..20fbd08714 100644
--- a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
@@ -23,7 +23,6 @@ import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.messaging.ConversationManager;
 import org.briarproject.api.plugins.ConnectionRegistry;
-import org.briarproject.api.sync.GroupId;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -53,9 +52,9 @@ public class ContactChooserFragment extends BaseFragment {
 	volatile ConnectionRegistry connectionRegistry;
 
 	public static ContactChooserFragment newInstance() {
-		
+
 		Bundle args = new Bundle();
-		
+
 		ContactChooserFragment fragment = new ContactChooserFragment();
 		fragment.setArguments(args);
 		return fragment;
@@ -132,14 +131,12 @@ public class ContactChooserFragment extends BaseFragment {
 							c1 = c;
 						} else {
 							ContactId id = c.getId();
-							GroupId groupId =
-									conversationManager.getConversationId(id);
 							GroupCount count =
 									conversationManager.getGroupCount(id);
 							boolean connected =
 									connectionRegistry.isConnected(c.getId());
 							contacts.add(new ContactListItem(c, connected,
-									groupId, count));
+									count));
 						}
 					}
 					displayContacts(contacts);
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
index 1d29ce89d9..44c57cb143 100644
--- a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
@@ -7,9 +7,7 @@ import org.briarproject.R;
 import org.briarproject.android.controller.handler.UiResultExceptionHandler;
 import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
 import org.briarproject.android.sharing.ContactSelectorActivity;
-import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 import org.jetbrains.annotations.NotNull;
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
index 2dad460eae..4c7d513895 100644
--- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
@@ -7,10 +7,7 @@ import android.support.annotation.UiThread;
 import org.briarproject.R;
 import org.briarproject.android.BriarActivity;
 import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
-import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.db.DatabaseExecutor;
-import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 
 import java.util.ArrayList;
@@ -58,10 +55,6 @@ public abstract class ContactSelectorActivity extends BriarActivity implements
 		this.contacts = contacts;
 	}
 
-	@DatabaseExecutor
-	public abstract boolean isDisabled(GroupId groupId, Contact c)
-			throws DbException;
-
 	static ArrayList<Integer> getContactsFromIds(
 			Collection<ContactId> contacts) {
 		// transform ContactIds to Integers so they can be added to a bundle
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
index 8016c45e90..48949b14cd 100644
--- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
@@ -11,7 +11,7 @@ import org.briarproject.api.sync.GroupId;
 
 import java.util.Collection;
 
-public interface ContactSelectorListener extends DestroyableContext {
+interface ContactSelectorListener extends DestroyableContext {
 
 	@Deprecated
 	void runOnDbThread(Runnable runnable);
diff --git a/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java b/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java
index 8bd5edf938..03a9ae3fce 100644
--- a/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java
+++ b/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java
@@ -8,26 +8,26 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 @NotThreadSafe
 @NotNullByDefault
-public class SelectableContactItem extends ContactItem {
+class SelectableContactItem extends ContactItem {
 
 	private boolean selected, disabled;
 
-	public SelectableContactItem(Contact contact, boolean connected,
+	SelectableContactItem(Contact contact, boolean connected,
 			boolean selected, boolean disabled) {
 		super(contact, connected);
 		this.selected = selected;
 		this.disabled = disabled;
 	}
 
-	public boolean isSelected() {
+	boolean isSelected() {
 		return selected;
 	}
 
-	public void toggleSelected() {
+	void toggleSelected() {
 		selected = !selected;
 	}
 
-	public boolean isDisabled() {
+	boolean isDisabled() {
 		return disabled;
 	}
 
diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
index ee6997f2e8..9bcd96e206 100644
--- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java
+++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
@@ -23,6 +23,7 @@ import android.widget.TextView;
 import org.briarproject.R;
 import org.briarproject.android.view.ArticleMovementMethod;
 import org.briarproject.android.widget.LinkDialogFragment;
+import org.briarproject.api.contact.ContactId;
 import org.briarproject.util.IoUtils;
 import org.briarproject.util.StringUtils;
 
@@ -173,4 +174,11 @@ public class AndroidUtils {
 		v.setMovementMethod(ArticleMovementMethod.getInstance());
 	}
 
+	public static String getAvatarTransitionName(ContactId c) {
+		return "avatar" + c.getInt();
+	}
+
+	public static String getBulbTransitionName(ContactId c) {
+		return "bulb" + c.getInt();
+	}
 }
diff --git a/briar-api/src/org/briarproject/api/messaging/ConversationManager.java b/briar-api/src/org/briarproject/api/messaging/ConversationManager.java
index 5695af95b2..a48a8ade84 100644
--- a/briar-api/src/org/briarproject/api/messaging/ConversationManager.java
+++ b/briar-api/src/org/briarproject/api/messaging/ConversationManager.java
@@ -4,7 +4,6 @@ import org.briarproject.api.clients.MessageTracker.GroupCount;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
-import org.briarproject.api.sync.GroupId;
 
 public interface ConversationManager {
 
@@ -14,9 +13,6 @@ public interface ConversationManager {
 	 */
 	void registerConversationClient(ConversationClient client);
 
-	/** Get the main group ID that represents this conversation */
-	GroupId getConversationId(ContactId contactId) throws DbException;
-
 	/** Get the unified group count for all private conversation messages. */
 	GroupCount getGroupCount(ContactId contactId) throws DbException;
 
diff --git a/briar-core/src/org/briarproject/messaging/ConversationManagerImpl.java b/briar-core/src/org/briarproject/messaging/ConversationManagerImpl.java
index dd90690789..fafd48e75c 100644
--- a/briar-core/src/org/briarproject/messaging/ConversationManagerImpl.java
+++ b/briar-core/src/org/briarproject/messaging/ConversationManagerImpl.java
@@ -1,15 +1,11 @@
 package org.briarproject.messaging;
 
-import org.briarproject.api.clients.ContactGroupFactory;
 import org.briarproject.api.clients.MessageTracker.GroupCount;
-import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.messaging.ConversationManager;
-import org.briarproject.api.sync.Group;
-import org.briarproject.api.sync.GroupId;
 
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -19,14 +15,11 @@ import javax.inject.Inject;
 class ConversationManagerImpl implements ConversationManager {
 
 	private final DatabaseComponent db;
-	private final ContactGroupFactory contactGroupFactory;
 	private final Set<ConversationClient> clients;
 
 	@Inject
-	ConversationManagerImpl(DatabaseComponent db,
-			ContactGroupFactory contactGroupFactory) {
+	ConversationManagerImpl(DatabaseComponent db) {
 		this.db = db;
-		this.contactGroupFactory = contactGroupFactory;
 		clients = new CopyOnWriteArraySet<ConversationClient>();
 	}
 
@@ -38,23 +31,6 @@ class ConversationManagerImpl implements ConversationManager {
 		}
 	}
 
-	@Override
-	public GroupId getConversationId(ContactId contactId) throws DbException {
-		// TODO we should probably transition this to its own group
-		//      and/or work with the ContactId in the UI instead
-		Contact contact;
-		Transaction txn = db.startTransaction(true);
-		try {
-			contact = db.getContact(txn, contactId);
-			db.commitTransaction(txn);
-		} finally {
-			db.endTransaction(txn);
-		}
-		Group group = contactGroupFactory
-				.createContactGroup(MessagingManagerImpl.CLIENT_ID, contact);
-		return group.getId();
-	}
-
 	@Override
 	public GroupCount getGroupCount(ContactId contactId)
 			throws DbException {
-- 
GitLab