diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 5627651aecb15d7f0ea76befa8c04d3c5b63302d..ff40912c11a418058129e70b78f733a07d05b2b5 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -23,9 +23,6 @@ import org.briarproject.android.controller.SetupControllerImpl;
 import org.briarproject.android.controller.TransportStateListener;
 import org.briarproject.android.forum.ForumController;
 import org.briarproject.android.forum.ForumControllerImpl;
-import org.briarproject.android.forum.ForumTestControllerImpl;
-
-import javax.inject.Named;
 
 import dagger.Module;
 import dagger.Provides;
@@ -103,14 +100,6 @@ public class ActivityModule {
 		return forumController;
 	}
 
-	@Named("ForumTestController")
-	@ActivityScope
-	@Provides
-	protected ForumController provideForumTestController(
-			ForumTestControllerImpl forumController) {
-		return forumController;
-	}
-
 	@ActivityScope
 	@Provides
 	BlogController provideBlogController(BlogControllerImpl blogController) {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 6b8c5a2b5e9d1e4cf0a09465b96b57c9693b604b..19ce6d499e232e43a5f2fcafbba4ad1325a7a738 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -1,27 +1,18 @@
 package org.briarproject.android.forum;
 
-import android.animation.Animator;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
-import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import org.briarproject.R;
@@ -30,52 +21,46 @@ import org.briarproject.android.BriarActivity;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.forum.ForumController.ForumPostListener;
+import org.briarproject.android.forum.NestedForumAdapter.OnNestedForumListener;
 import org.briarproject.android.sharing.ShareForumActivity;
 import org.briarproject.android.sharing.SharingStatusForumActivity;
-import org.briarproject.android.view.AuthorView;
 import org.briarproject.android.view.BriarRecyclerView;
 import org.briarproject.android.view.TextInputView;
 import org.briarproject.android.view.TextInputView.TextInputListener;
 import org.briarproject.api.forum.Forum;
+import org.briarproject.api.forum.ForumPost;
+import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.MessageId;
 import org.briarproject.util.StringUtils;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import javax.inject.Inject;
 
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
 import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.widget.Toast.LENGTH_SHORT;
 
 public class ForumActivity extends BriarActivity implements
-		ForumPostListener, TextInputListener {
+		ForumPostListener, TextInputListener, OnNestedForumListener {
 
 	static final String FORUM_NAME = "briar.FORUM_NAME";
 
 	private static final int REQUEST_FORUM_SHARED = 3;
-	private static final int UNDEFINED = -1;
 	private static final String KEY_INPUT_VISIBILITY = "inputVisibility";
 	private static final String KEY_REPLY_ID = "replyId";
 
 	@Inject
 	AndroidNotificationManager notificationManager;
 
-	// uncomment the next line for a test component with dummy data
-//	@Named("ForumTestController")
 	@Inject
 	protected ForumController forumController;
 
 	// Protected access for testing
-	protected ForumAdapter forumAdapter;
+	protected NestedForumAdapter forumAdapter;
 
 	private BriarRecyclerView recyclerView;
 	private TextInputView textInput;
@@ -96,42 +81,42 @@ public class ForumActivity extends BriarActivity implements
 		String forumName = i.getStringExtra(FORUM_NAME);
 		if (forumName != null) setTitle(forumName);
 
-		forumAdapter = new ForumAdapter();
-
 		textInput = (TextInputView) findViewById(R.id.text_input_container);
 		textInput.setVisibility(GONE);
 		textInput.setListener(this);
 		recyclerView =
 				(BriarRecyclerView) findViewById(R.id.forum_discussion_list);
-		recyclerView.setAdapter(forumAdapter);
-		linearLayoutManager = new LinearLayoutManager(this);
+		LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
 		recyclerView.setLayoutManager(linearLayoutManager);
+		forumAdapter = new NestedForumAdapter(this, this, linearLayoutManager);
+		recyclerView.setAdapter(forumAdapter);
 		recyclerView.setEmptyText(R.string.no_forum_posts);
 
-		forumController.loadForum(groupId, new UiResultHandler<Boolean>(this) {
-			@Override
-			public void onResultUi(Boolean result) {
-				if (result) {
-					Forum forum = forumController.getForum();
-					if (forum != null) setTitle(forum.getName());
-					List<ForumEntry> entries =
-							forumController.getForumEntries();
-					if (entries.isEmpty()) {
-						recyclerView.showData();
-					} else {
-						forumAdapter.setEntries(entries);
-						if (state != null) {
-							byte[] replyId = state.getByteArray(KEY_REPLY_ID);
-							if (replyId != null)
-								forumAdapter.setReplyEntryById(replyId);
+		forumController.loadForum(groupId,
+				new UiResultHandler<List<ForumEntry>>(this) {
+					@Override
+					public void onResultUi(List<ForumEntry> result) {
+						if (result != null) {
+							Forum forum = forumController.getForum();
+							if (forum != null) setTitle(forum.getName());
+							List<ForumEntry> entries = new ArrayList<>(result);
+							if (entries.isEmpty()) {
+								recyclerView.showData();
+							} else {
+								forumAdapter.setEntries(entries);
+								if (state != null) {
+									byte[] replyId =
+											state.getByteArray(KEY_REPLY_ID);
+									if (replyId != null)
+										forumAdapter.setReplyEntryById(replyId);
+								}
+							}
+						} else {
+							// TODO Improve UX ?
+							finish();
 						}
 					}
-				} else {
-					// TODO Maybe an error dialog ?
-					finish();
-				}
-			}
-		});
+				});
 	}
 
 	@Override
@@ -265,12 +250,27 @@ public class ForumActivity extends BriarActivity implements
 			return;
 		if (forumController.getForum() == null) return;
 		ForumEntry replyEntry = forumAdapter.getReplyEntry();
+		UiResultHandler<ForumPost> resultHandler =
+				new UiResultHandler<ForumPost>(this) {
+					@Override
+					public void onResultUi(ForumPost result) {
+						forumController.storePost(result,
+								new UiResultHandler<ForumEntry>(
+										ForumActivity.this) {
+									@Override
+									public void onResultUi(ForumEntry result) {
+										onForumEntryAdded(result, true);
+									}
+								});
+					}
+				};
 		if (replyEntry == null) {
 			// root post
-			forumController.createPost(StringUtils.toUtf8(text));
+			forumController.createPost(StringUtils.toUtf8(text), resultHandler);
 		} else {
-			forumController.createPost(StringUtils.toUtf8(text),
-					replyEntry.getMessageId());
+			forumController
+					.createPost(StringUtils.toUtf8(text), replyEntry.getId(),
+							resultHandler);
 		}
 		hideSoftKeyboard(textInput);
 		textInput.setVisibility(GONE);
@@ -308,412 +308,47 @@ public class ForumActivity extends BriarActivity implements
 	}
 
 	@Override
-	public void addLocalEntry(int index, ForumEntry entry) {
-		forumAdapter.addEntry(index, entry, true);
-		displaySnackbarShort(R.string.forum_new_entry_posted);
+	public void onEntryVisible(ForumEntry forumEntry) {
+		if (!forumEntry.isRead()) {
+			forumEntry.setRead(true);
+			forumController.entryRead(forumEntry);
+		}
 	}
 
 	@Override
-	public void addForeignEntry(final int index, final ForumEntry entry) {
-		forumAdapter.addEntry(index, entry, false);
-		Snackbar snackbar =
-				Snackbar.make(recyclerView, R.string.forum_new_entry_received,
-						Snackbar.LENGTH_LONG);
-		snackbar.setActionTextColor(
-				ContextCompat.getColor(this, R.color.briar_button_positive));
-		snackbar.setAction(R.string.show, new View.OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				forumAdapter.scrollToEntry(entry);
-			}
-		});
-		snackbar.getView().setBackgroundResource(R.color.briar_primary);
-		snackbar.show();
+	public void onReplyClick(ForumEntry forumEntry) {
+		showTextInput(forumEntry);
 	}
 
-	static class ForumViewHolder extends RecyclerView.ViewHolder {
-
-		final TextView textView, lvlText, repliesText;
-		final AuthorView author;
-		final View[] lvls;
-		final View chevron, replyButton;
-		final ViewGroup cell;
-		final View topDivider;
-
-		ForumViewHolder(View v) {
-			super(v);
-
-			textView = (TextView) v.findViewById(R.id.text);
-			lvlText = (TextView) v.findViewById(R.id.nested_line_text);
-			author = (AuthorView) v.findViewById(R.id.author);
-			repliesText = (TextView) v.findViewById(R.id.replies);
-			int[] nestedLineIds = {
-					R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
-					R.id.nested_line_4, R.id.nested_line_5
-			};
-			lvls = new View[nestedLineIds.length];
-			for (int i = 0; i < lvls.length; i++) {
-				lvls[i] = v.findViewById(nestedLineIds[i]);
-			}
-			chevron = v.findViewById(R.id.chevron);
-			replyButton = v.findViewById(R.id.btn_reply);
-			cell = (ViewGroup) v.findViewById(R.id.forum_cell);
-			topDivider = v.findViewById(R.id.top_divider);
-		}
-	}
-
-	public class ForumAdapter extends RecyclerView.Adapter<ForumViewHolder> {
-
-		private final List<ForumEntry> forumEntries = new ArrayList<>();
-		private final Map<ForumEntry, ValueAnimator> animatingEntries =
-				new HashMap<>();
-
-		// highlight not dependant on time
-		private ForumEntry replyEntry;
-		// temporary highlight
-		private ForumEntry addedEntry;
-
-		private ForumEntry getReplyEntry() {
-			return replyEntry;
-		}
-
-		void setEntries(List<ForumEntry> entries) {
-			forumEntries.clear();
-			forumEntries.addAll(entries);
-			notifyItemRangeInserted(0, entries.size());
-		}
-
-		void addEntry(int index, ForumEntry entry, boolean isScrolling) {
-			forumEntries.add(index, entry);
-			boolean isShowingDescendants = false;
-			if (entry.getLevel() > 0) {
-				// update parent and make sure descendants are visible
-				// Note that the parent's visibility is guaranteed (otherwise
-				// the reply button would not be visible)
-				for (int i = index - 1; i >= 0; i--) {
-					ForumEntry higherEntry = forumEntries.get(i);
-					if (higherEntry.getLevel() < entry.getLevel()) {
-						// parent found
-						if (!higherEntry.isShowingDescendants()) {
-							isShowingDescendants = true;
-							showDescendants(higherEntry);
-						}
-						notifyItemChanged(getVisiblePos(higherEntry));
-						break;
-					}
-				}
-			}
-			if (!isShowingDescendants) {
-				int visiblePos = getVisiblePos(entry);
-				notifyItemInserted(visiblePos);
-				if (isScrolling)
-					linearLayoutManager
-							.scrollToPositionWithOffset(visiblePos, 0);
-			}
-			addedEntry = entry;
-		}
-
-		void scrollToEntry(ForumEntry entry) {
-			int visiblePos = getVisiblePos(entry);
-			linearLayoutManager.scrollToPositionWithOffset(visiblePos, 0);
-		}
-
-		private boolean hasDescendants(ForumEntry forumEntry) {
-			int i = forumEntries.indexOf(forumEntry);
-			if (i >= 0 && i < forumEntries.size() - 1) {
-				if (forumEntries.get(i + 1).getLevel() >
-						forumEntry.getLevel()) {
-					return true;
-				}
-			}
-			return false;
-		}
-
-		private boolean hasVisibleDescendants(ForumEntry forumEntry) {
-			int visiblePos = getVisiblePos(forumEntry);
-			int levelLimit = forumEntry.getLevel();
-			// FIXME This loop doesn't really loop. @ernir please review!
-			for (int i = visiblePos + 1; i < getItemCount(); i++) {
-				ForumEntry entry = getVisibleEntry(i);
-				if (entry != null && entry.getLevel() <= levelLimit)
-					break;
-				return true;
-			}
-			return false;
-		}
-
-		private int getReplyCount(ForumEntry entry) {
-			int counter = 0;
-			int pos = forumEntries.indexOf(entry);
-			if (pos >= 0) {
-				int ancestorLvl = forumEntries.get(pos).getLevel();
-				for (int i = pos + 1; i < forumEntries.size(); i++) {
-					int descendantLvl = forumEntries.get(i).getLevel();
-					if (descendantLvl <= ancestorLvl)
-						break;
-					if (descendantLvl == ancestorLvl + 1)
-						counter++;
-				}
-			}
-			return counter;
-		}
-
-		void setReplyEntryById(byte[] id) {
-			MessageId messageId = new MessageId(id);
-			for (ForumEntry entry : forumEntries) {
-				if (entry.getMessageId().equals(messageId)) {
-					setReplyEntry(entry);
-					break;
-				}
-			}
-		}
-
-		void setReplyEntry(ForumEntry entry) {
-			if (replyEntry != null) {
-				notifyItemChanged(getVisiblePos(replyEntry));
-			}
-			replyEntry = entry;
-			if (replyEntry != null) {
-				notifyItemChanged(getVisiblePos(replyEntry));
-			}
-		}
-
-		private List<Integer> getSubTreeIndexes(int pos, int levelLimit) {
-			List<Integer> indexList = new ArrayList<>();
-
-			for (int i = pos + 1; i < getItemCount(); i++) {
-				ForumEntry entry = getVisibleEntry(i);
-				if (entry != null && entry.getLevel() > levelLimit) {
-					indexList.add(i);
-				} else {
-					break;
-				}
-			}
-			return indexList;
-		}
-
-		void showDescendants(ForumEntry forumEntry) {
-			forumEntry.setShowingDescendants(true);
-			int visiblePos = getVisiblePos(forumEntry);
-			List<Integer> indexList =
-					getSubTreeIndexes(visiblePos, forumEntry.getLevel());
-			if (!indexList.isEmpty()) {
-				if (indexList.size() == 1) {
-					notifyItemInserted(indexList.get(0));
-				} else {
-					notifyItemRangeInserted(indexList.get(0),
-							indexList.size());
-				}
-			}
-		}
-
-		void hideDescendants(ForumEntry forumEntry) {
-			int visiblePos = getVisiblePos(forumEntry);
-			List<Integer> indexList =
-					getSubTreeIndexes(visiblePos, forumEntry.getLevel());
-			if (!indexList.isEmpty()) {
-				// stop animating children
-				for (int index : indexList) {
-					ValueAnimator anim =
-							animatingEntries.get(forumEntries.get(index));
-					if (anim != null && anim.isRunning()) {
-						anim.cancel();
-					}
-				}
-				if (indexList.size() == 1) {
-					notifyItemRemoved(indexList.get(0));
-				} else {
-					notifyItemRangeRemoved(indexList.get(0),
-							indexList.size());
-				}
-			}
-			forumEntry.setShowingDescendants(false);
-		}
-
-
-		@Nullable
-		ForumEntry getVisibleEntry(int position) {
-			int levelLimit = UNDEFINED;
-			for (ForumEntry forumEntry : forumEntries) {
-				if (levelLimit >= 0) {
-					if (forumEntry.getLevel() > levelLimit) {
-						continue;
-					}
-					levelLimit = UNDEFINED;
-				}
-				if (!forumEntry.isShowingDescendants()) {
-					levelLimit = forumEntry.getLevel();
-				}
-				if (position-- == 0) {
-					return forumEntry;
-				}
-			}
-			return null;
-		}
-
-		private void animateFadeOut(final ForumViewHolder ui,
-				final ForumEntry addedEntry) {
-			ui.setIsRecyclable(false);
-			ValueAnimator anim = new ValueAnimator();
-			animatingEntries.put(addedEntry, anim);
-			ColorDrawable viewColor = (ColorDrawable) ui.cell.getBackground();
-			anim.setIntValues(viewColor.getColor(), ContextCompat
+	private void onForumEntryAdded(final ForumEntry entry, boolean isLocal) {
+		forumAdapter.addEntry(entry);
+		if (isLocal) {
+			displaySnackbarShort(R.string.forum_new_entry_posted);
+		} else {
+			Snackbar snackbar = Snackbar.make(recyclerView,
+					R.string.forum_new_entry_received, Snackbar.LENGTH_LONG);
+			snackbar.setActionTextColor(ContextCompat
 					.getColor(ForumActivity.this,
-							R.color.window_background));
-			anim.setEvaluator(new ArgbEvaluator());
-			anim.addListener(new Animator.AnimatorListener() {
-				@Override
-				public void onAnimationStart(Animator animation) {
-
-				}
-
-				@Override
-				public void onAnimationEnd(Animator animation) {
-					ui.setIsRecyclable(true);
-					animatingEntries.remove(addedEntry);
-				}
-
-				@Override
-				public void onAnimationCancel(Animator animation) {
-					ui.setIsRecyclable(true);
-					animatingEntries.remove(addedEntry);
-				}
-
-				@Override
-				public void onAnimationRepeat(Animator animation) {
-
-				}
-			});
-			anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-				@Override
-				public void onAnimationUpdate(ValueAnimator valueAnimator) {
-					ui.cell.setBackgroundColor(
-							(Integer) valueAnimator.getAnimatedValue());
-				}
-			});
-			anim.setDuration(5000);
-			anim.start();
-		}
-
-		@Override
-		public ForumViewHolder onCreateViewHolder(ViewGroup parent,
-				int viewType) {
-			View v = LayoutInflater.from(parent.getContext())
-					.inflate(R.layout.list_item_forum_post, parent, false);
-			return new ForumViewHolder(v);
-		}
-
-		@Override
-		public void onBindViewHolder(
-				final ForumViewHolder ui, final int position) {
-			final ForumEntry data = getVisibleEntry(position);
-			if (data == null) return;
-
-			if (!data.isRead()) {
-				data.setRead(true);
-				forumController.entryRead(data);
-			}
-			ui.textView.setText(StringUtils.trim(data.getText()));
-
-			if (position == 0) {
-				ui.topDivider.setVisibility(View.INVISIBLE);
-			} else {
-				ui.topDivider.setVisibility(View.VISIBLE);
-			}
-
-			for (int i = 0; i < ui.lvls.length; i++) {
-				ui.lvls[i].setVisibility(i < data.getLevel() ? VISIBLE : GONE);
-			}
-			if (data.getLevel() > 5) {
-				ui.lvlText.setVisibility(VISIBLE);
-				ui.lvlText.setText("" + data.getLevel());
-			} else {
-				ui.lvlText.setVisibility(GONE);
-			}
-			ui.author.setAuthor(data.getAuthor());
-			ui.author.setDate(data.getTimestamp());
-			ui.author.setAuthorStatus(data.getStatus());
-
-			int replies = getReplyCount(data);
-			if (replies == 0) {
-				ui.repliesText.setText("");
-			} else {
-				ui.repliesText.setText(getResources()
-						.getQuantityString(R.plurals.message_replies, replies,
-								replies));
-			}
-
-			if (hasDescendants(data)) {
-				ui.chevron.setVisibility(VISIBLE);
-				if (hasVisibleDescendants(data)) {
-					ui.chevron.setSelected(false);
-				} else {
-					ui.chevron.setSelected(true);
-				}
-				ui.chevron.setOnClickListener(new View.OnClickListener() {
-					@Override
-					public void onClick(View v) {
-						ui.chevron.setSelected(!ui.chevron.isSelected());
-						if (ui.chevron.isSelected()) {
-							hideDescendants(data);
-						} else {
-							showDescendants(data);
-						}
-					}
-				});
-			} else {
-				ui.chevron.setVisibility(INVISIBLE);
-			}
-			if (data.equals(replyEntry)) {
-				ui.cell.setBackgroundColor(ContextCompat
-						.getColor(ForumActivity.this,
-								R.color.forum_cell_highlight));
-			} else if (data.equals(addedEntry)) {
-
-				ui.cell.setBackgroundColor(ContextCompat
-						.getColor(ForumActivity.this,
-								R.color.forum_cell_highlight));
-				animateFadeOut(ui, addedEntry);
-				addedEntry = null;
-			} else {
-				ui.cell.setBackgroundColor(ContextCompat
-						.getColor(ForumActivity.this,
-								R.color.window_background));
-			}
-			ui.replyButton.setOnClickListener(new View.OnClickListener() {
+							R.color.briar_button_positive));
+			snackbar.setAction(R.string.show, new View.OnClickListener() {
 				@Override
 				public void onClick(View v) {
-					showTextInput(data);
-					linearLayoutManager
-							.scrollToPositionWithOffset(getVisiblePos(data), 0);
+					forumAdapter.scrollToEntry(entry);
 				}
 			});
+			snackbar.getView().setBackgroundResource(R.color.briar_primary);
+			snackbar.show();
 		}
+	}
 
-		private int getVisiblePos(ForumEntry sEntry) {
-			int visibleCounter = 0;
-			int levelLimit = UNDEFINED;
-			for (ForumEntry fEntry : forumEntries) {
-				if (levelLimit >= 0) {
-					if (fEntry.getLevel() > levelLimit) {
-						continue;
-					}
-					levelLimit = UNDEFINED;
-				}
-				if (sEntry != null && sEntry.equals(fEntry)) {
-					return visibleCounter;
-				} else if (!fEntry.isShowingDescendants()) {
-					levelLimit = fEntry.getLevel();
-				}
-				visibleCounter++;
+	@Override
+	public void onExternalEntryAdded(ForumPostHeader header) {
+		forumController.loadPost(header, new UiResultHandler<ForumEntry>(this) {
+			@Override
+			public void onResultUi(final ForumEntry result) {
+				onForumEntryAdded(result, false);
 			}
-			return sEntry == null ? visibleCounter : NO_POSITION;
-		}
+		});
 
-		@Override
-		public int getItemCount() {
-			return getVisiblePos(null);
-		}
 	}
-
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ForumController.java b/briar-android/src/org/briarproject/android/forum/ForumController.java
index b492627e41e07a5ba18e7bd971484cc21cea91d1..79499d48dea283ef68a3146e73ee39742544387a 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumController.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumController.java
@@ -1,10 +1,13 @@
 package org.briarproject.android.forum;
 
 import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
 
 import org.briarproject.android.controller.ActivityLifecycleController;
 import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.forum.Forum;
+import org.briarproject.api.forum.ForumPost;
+import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 
@@ -13,12 +16,14 @@ import java.util.List;
 
 public interface ForumController extends ActivityLifecycleController {
 
-	void loadForum(GroupId groupId, ResultHandler<Boolean> resultHandler);
+	void loadForum(GroupId groupId,
+			ResultHandler<List<ForumEntry>> resultHandler);
 
 	@Nullable
 	Forum getForum();
 
-	List<ForumEntry> getForumEntries();
+	void loadPost(ForumPostHeader header,
+			ResultHandler<ForumEntry> resultHandler);
 
 	void unsubscribe(ResultHandler<Boolean> resultHandler);
 
@@ -26,14 +31,16 @@ public interface ForumController extends ActivityLifecycleController {
 
 	void entriesRead(Collection<ForumEntry> messageIds);
 
-	void createPost(byte[] body);
+	void createPost(byte[] body, ResultHandler<ForumPost> resultHandler);
 
-	void createPost(byte[] body, MessageId parentId);
+	void createPost(byte[] body, MessageId parentId,
+			ResultHandler<ForumPost> resultHandler);
 
-	interface ForumPostListener {
-		void addLocalEntry(int index, ForumEntry entry);
+	void storePost(ForumPost post, ResultHandler<ForumEntry> resultHandler);
 
-		void addForeignEntry(int index, ForumEntry entry);
+	interface ForumPostListener {
+		@UiThread
+		void onExternalEntryAdded(ForumPostHeader header);
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
index 2ec3bc09be9256e004949e1446c77f04aeffa0bc..51e23cfc4aaa07d3ce43bfb6ed1ea0f7007c8c5f 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
@@ -6,7 +6,6 @@ import android.support.annotation.Nullable;
 import org.briarproject.android.controller.DbControllerImpl;
 import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.FormatException;
-import org.briarproject.api.clients.MessageTree;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.crypto.KeyParser;
@@ -26,7 +25,6 @@ import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
-import org.briarproject.clients.MessageTreeImpl;
 import org.briarproject.util.StringUtils;
 
 import java.security.GeneralSecurityException;
@@ -35,9 +33,9 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Stack;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Logger;
 
 import javax.inject.Inject;
@@ -69,12 +67,9 @@ public class ForumControllerImpl extends DbControllerImpl
 	protected volatile IdentityManager identityManager;
 
 	private final Map<MessageId, byte[]> bodyCache = new ConcurrentHashMap<>();
-	private final MessageTree<ForumPostHeader> tree = new MessageTreeImpl<>();
-
+	private volatile AtomicLong newestTimeStamp = new AtomicLong();
 	private volatile LocalAuthor localAuthor = null;
 	private volatile Forum forum = null;
-	// FIXME: This collection isn't thread-safe, isn't updated atomically
-	private volatile List<ForumEntry> forumEntries = null;
 
 	private ForumPostListener listener;
 
@@ -111,13 +106,21 @@ public class ForumControllerImpl extends DbControllerImpl
 	@Override
 	public void eventOccurred(Event e) {
 		if (forum == null) return;
-
 		if (e instanceof ForumPostReceivedEvent) {
-			ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
+			final ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
 			if (pe.getGroupId().equals(forum.getId())) {
 				LOG.info("Forum Post received, adding...");
-				// FIXME: Don't make blocking calls in event handlers
-				addNewPost(pe.getForumPostHeader());
+				final ForumPostHeader fph = pe.getForumPostHeader();
+				activity.runOnUiThread(new Runnable() {
+					@Override
+					public void run() {
+						synchronized (this) {
+							if (fph.getTimestamp() > newestTimeStamp.get())
+								newestTimeStamp.set(fph.getTimestamp());
+						}
+						listener.onExternalEntryAdded(fph);
+					}
+				});
 			}
 		} else if (e instanceof GroupRemovedEvent) {
 			GroupRemovedEvent s = (GroupRemovedEvent) e;
@@ -133,46 +136,37 @@ public class ForumControllerImpl extends DbControllerImpl
 		}
 	}
 
-	private void addNewPost(final ForumPostHeader h) {
-		if (forum == null) return;
-		runOnDbThread(new Runnable() {
-			@Override
-			public void run() {
-				if (!bodyCache.containsKey(h.getId())) {
-					try {
-						byte[] body = forumManager.getPostBody(h.getId());
-						bodyCache.put(h.getId(), body);
-					} catch (DbException e) {
-						if (LOG.isLoggable(WARNING))
-							LOG.log(WARNING, e.toString(), e);
-						return;
-					}
-				}
+	/**
+	 * This should only be run from the DbThread.
+	 *
+	 * @throws DbException
+	 */
+	private void loadForum(GroupId groupId) throws DbException {
+		// Get Forum
+		long now = System.currentTimeMillis();
+		forum = forumManager.getForum(groupId);
+		long duration = System.currentTimeMillis() - now;
+		if (LOG.isLoggable(INFO))
+			LOG.info("Loading forum took " + duration +
+					" ms");
 
-				tree.add(h);
-				forumEntries = null;
-				// FIXME we should not need to calculate the index here
-				//       the index is essentially stored in two different locations
-				int i = 0;
-				for (ForumEntry entry : getForumEntries()) {
-					if (entry.getMessageId().equals(h.getId())) {
-						if (localAuthor != null && localAuthor.equals(h.getAuthor())) {
-							addLocalEntry(i, entry);
-						} else {
-							addForeignEntry(i, entry);
-						}
-					}
-					i++;
-				}
-			}
-		});
+		// Get First Identity
+		now = System.currentTimeMillis();
+		localAuthor =
+				identityManager.getLocalAuthors().iterator()
+						.next();
+		duration = System.currentTimeMillis() - now;
+		if (LOG.isLoggable(INFO))
+			LOG.info("Loading author took " + duration +
+					" ms");
 	}
 
 	/**
 	 * This should only be run from the DbThread.
+	 *
 	 * @throws DbException
 	 */
-	private void loadPosts() throws DbException {
+	private Collection<ForumPostHeader> loadHeaders() throws DbException {
 		if (forum == null)
 			throw new RuntimeException("Forum has not been initialized");
 
@@ -180,59 +174,71 @@ public class ForumControllerImpl extends DbControllerImpl
 		long now = System.currentTimeMillis();
 		Collection<ForumPostHeader> headers =
 				forumManager.getPostHeaders(forum.getId());
-		tree.add(headers);
 		long duration = System.currentTimeMillis() - now;
 		if (LOG.isLoggable(INFO))
 			LOG.info("Loading headers took " + duration + " ms");
+		return headers;
+	}
 
+	/**
+	 * This should only be run from the DbThread.
+	 *
+	 * @throws DbException
+	 */
+	private void loadBodies(Collection<ForumPostHeader> headers)
+			throws DbException {
 		// Get Bodies
-		now = System.currentTimeMillis();
+		long now = System.currentTimeMillis();
 		for (ForumPostHeader header : headers) {
 			if (!bodyCache.containsKey(header.getId())) {
 				byte[] body = forumManager.getPostBody(header.getId());
 				bodyCache.put(header.getId(), body);
 			}
 		}
-		duration = System.currentTimeMillis() - now;
+		long duration = System.currentTimeMillis() - now;
 		if (LOG.isLoggable(INFO))
 			LOG.info("Loading bodies took " + duration + " ms");
 	}
 
+	private List<ForumEntry> buildForumEntries(
+			Collection<ForumPostHeader> headers) {
+		List<ForumEntry> entries = new ArrayList<>();
+		for (ForumPostHeader h : headers) {
+			byte[] body = bodyCache.get(h.getId());
+			entries.add(new ForumEntry(h, StringUtils.fromUtf8(body)));
+		}
+		return entries;
+	}
+
+	private synchronized void checkNewestTimeStamp(
+			Collection<ForumPostHeader> headers) {
+		for (ForumPostHeader h : headers) {
+			if (h.getTimestamp() > newestTimeStamp.get())
+				newestTimeStamp.set(h.getTimestamp());
+		}
+	}
+
 	@Override
 	public void loadForum(final GroupId groupId,
-			final ResultHandler<Boolean> resultHandler) {
+			final ResultHandler<List<ForumEntry>> resultHandler) {
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
-				LOG.info("Loading forum...");
+				if (LOG.isLoggable(INFO))
+					LOG.info("Loading forum...");
 				try {
 					if (forum == null) {
-						// Get Forum
-						long now = System.currentTimeMillis();
-						forum = forumManager.getForum(groupId);
-						long duration = System.currentTimeMillis() - now;
-						if (LOG.isLoggable(INFO))
-							LOG.info("Loading forum took " + duration +
-									" ms");
-
-						// Get First Identity
-						now = System.currentTimeMillis();
-						localAuthor =
-								identityManager.getLocalAuthors().iterator()
-										.next();
-						duration = System.currentTimeMillis() - now;
-						if (LOG.isLoggable(INFO))
-							LOG.info("Loading author took " + duration +
-									" ms");
-
-						// Get Forum Posts and Bodies
-						loadPosts();
+						loadForum(groupId);
 					}
-					resultHandler.onResult(true);
+					// Get Forum Posts and Bodies
+					Collection<ForumPostHeader> headers = loadHeaders();
+					checkNewestTimeStamp(headers);
+					loadBodies(headers);
+					resultHandler.onResult(buildForumEntries(headers));
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-					resultHandler.onResult(false);
+					resultHandler.onResult(null);
 				}
 			}
 		});
@@ -245,31 +251,21 @@ public class ForumControllerImpl extends DbControllerImpl
 	}
 
 	@Override
-	public List<ForumEntry> getForumEntries() {
-		if (forumEntries != null) {
-			return forumEntries;
-		}
-		Collection<ForumPostHeader> headers = getHeaders();
-		List<ForumEntry> entries = new ArrayList<>();
-		Stack<MessageId> idStack = new Stack<>();
-
-		for (ForumPostHeader h : headers) {
-			if (h.getParentId() == null) {
-				idStack.clear();
-			} else if (idStack.isEmpty() ||
-					!idStack.contains(h.getParentId())) {
-				idStack.push(h.getParentId());
-			} else if (!h.getParentId().equals(idStack.peek())) {
-				do {
-					idStack.pop();
-				} while (!h.getParentId().equals(idStack.peek()));
+	public void loadPost(final ForumPostHeader header,
+			final ResultHandler<ForumEntry> resultHandler) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				LOG.info("Loading post...");
+				try {
+					loadBodies(Collections.singletonList(header));
+					resultHandler.onResult(new ForumEntry(header, StringUtils
+							.fromUtf8(bodyCache.get(header.getId()))));
+				} catch (DbException e) {
+					e.printStackTrace();
+				}
 			}
-			byte[] body = bodyCache.get(h.getId());
-			entries.add(new ForumEntry(h, StringUtils.fromUtf8(body),
-						idStack.size()));
-		}
-		forumEntries = entries;
-		return entries;
+		});
 	}
 
 	@Override
@@ -307,7 +303,7 @@ public class ForumControllerImpl extends DbControllerImpl
 				try {
 					long now = System.currentTimeMillis();
 					for (ForumEntry fe : forumEntries) {
-						forumManager.setReadFlag(fe.getMessageId(), true);
+						forumManager.setReadFlag(fe.getId(), true);
 					}
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
@@ -321,77 +317,67 @@ public class ForumControllerImpl extends DbControllerImpl
 	}
 
 	@Override
-	public void createPost(byte[] body) {
-		createPost(body, null);
+	public void createPost(byte[] body,
+			ResultHandler<ForumPost> resultHandler) {
+		createPost(body, null, resultHandler);
 	}
 
 	@Override
-	public void createPost(final byte[] body, final MessageId parentId) {
+	public void createPost(final byte[] body, final MessageId parentId,
+			final ResultHandler<ForumPost> resultHandler) {
 		cryptoExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
+				if (LOG.isLoggable(INFO))
+					LOG.info("create post..");
 				long timestamp = System.currentTimeMillis();
-				long newestTimeStamp = 0;
-				Collection<ForumPostHeader> headers = getHeaders();
-				if (headers != null) {
-					for (ForumPostHeader h : headers) {
-						if (h.getTimestamp() > newestTimeStamp)
-							newestTimeStamp = h.getTimestamp();
-					}
-				}
-				// Don't use an earlier timestamp than the newest post
-				if (timestamp < newestTimeStamp) {
-					timestamp = newestTimeStamp;
-				}
+				// FIXME next two lines Synchronized ?
+				// Only reading the atomic value, and even if it is changed
+				// between the first and second get, the condition will hold
+				if (timestamp < newestTimeStamp.get())
+					timestamp = newestTimeStamp.get();
 				ForumPost p;
 				try {
 					KeyParser keyParser = crypto.getSignatureKeyParser();
 					byte[] b = localAuthor.getPrivateKey();
 					PrivateKey authorKey = keyParser.parsePrivateKey(b);
 					p = forumPostFactory.createPseudonymousPost(
-							forum.getId(), timestamp, parentId,
-							localAuthor, "text/plain", body,
-							authorKey);
+							forum.getId(), timestamp, parentId, localAuthor,
+							"text/plain", body, authorKey);
 				} catch (GeneralSecurityException | FormatException e) {
 					throw new RuntimeException(e);
 				}
 				bodyCache.put(p.getMessage().getId(), body);
-				storePost(p);
-				// FIXME: Don't make DB calls on the crypto executor
-				addNewPost(p);
-			}
-		});
-	}
-
-	private void addLocalEntry(final int index, final ForumEntry entry) {
-		activity.runOnUiThread(new Runnable() {
-			@Override
-			public void run() {
-				listener.addLocalEntry(index, entry);
-			}
-		});
-	}
-
-	private void addForeignEntry(final int index, final ForumEntry entry) {
-		activity.runOnUiThread(new Runnable() {
-			@Override
-			public void run() {
-				listener.addForeignEntry(index, entry);
+				resultHandler.onResult(p);
 			}
 		});
 	}
 
-	private void storePost(final ForumPost p) {
+	public void storePost(final ForumPost p,
+			final ResultHandler<ForumEntry> resultHandler) {
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
 				try {
+					if (LOG.isLoggable(INFO))
+						LOG.info("Store post...");
 					long now = System.currentTimeMillis();
 					forumManager.addLocalPost(p);
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
 						LOG.info(
 								"Storing message took " + duration + " ms");
+
+					ForumPostHeader h =
+							new ForumPostHeader(p.getMessage().getId(),
+									p.getParent(),
+									p.getMessage().getTimestamp(),
+									p.getAuthor(), VERIFIED,
+									true);
+
+					resultHandler.onResult(new ForumEntry(h, StringUtils
+							.fromUtf8(bodyCache.get(p.getMessage().getId()))));
+
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
@@ -400,16 +386,4 @@ public class ForumControllerImpl extends DbControllerImpl
 		});
 	}
 
-	private void addNewPost(final ForumPost p) {
-		ForumPostHeader h =
-				new ForumPostHeader(p.getMessage().getId(), p.getParent(),
-						p.getMessage().getTimestamp(), p.getAuthor(), VERIFIED,
-						false);
-		addNewPost(h);
-	}
-
-	private Collection<ForumPostHeader> getHeaders() {
-		return tree.depthFirstOrder();
-	}
-
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ForumEntry.java b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
index f809d72394ddf9144ff1fc0642b32d34459673dd..889ef9802e5be778d8210707903ff99a7b74bb2f 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumEntry.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
@@ -1,33 +1,36 @@
 package org.briarproject.android.forum;
 
+import org.briarproject.api.clients.MessageTree;
 import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
-import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.MessageId;
 
-public class ForumEntry {
+public class ForumEntry implements MessageTree.MessageNode {
+
+	public final static int LEVEL_UNDEFINED = -1;
 
 	private final MessageId messageId;
+	private final MessageId parentId;
 	private final String text;
-	private final int level;
 	private final long timestamp;
 	private final Author author;
 	private Status status;
+	private int level = LEVEL_UNDEFINED;
 	private boolean isShowingDescendants = true;
 	private boolean isRead = true;
 
-	ForumEntry(ForumPostHeader h, String text, int level) {
-		this(h.getId(), text, level, h.getTimestamp(), h.getAuthor(),
+	ForumEntry(ForumPostHeader h, String text) {
+		this(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(),
 				h.getAuthorStatus());
 		this.isRead = h.isRead();
 	}
 
-	public ForumEntry(MessageId messageId, String text, int level,
+	public ForumEntry(MessageId messageId, MessageId parentId, String text,
 			long timestamp, Author author, Status status) {
 		this.messageId = messageId;
+		this.parentId = parentId;
 		this.text = text;
-		this.level = level;
 		this.timestamp = timestamp;
 		this.author = author;
 		this.status = status;
@@ -41,6 +44,16 @@ public class ForumEntry {
 		return level;
 	}
 
+	@Override
+	public MessageId getId() {
+		return messageId;
+	}
+
+	@Override
+	public MessageId getParentId() {
+		return parentId;
+	}
+
 	public long getTimestamp() {
 		return timestamp;
 	}
@@ -57,6 +70,10 @@ public class ForumEntry {
 		return isShowingDescendants;
 	}
 
+	void setLevel(int level) {
+		this.level = level;
+	}
+
 	void setShowingDescendants(boolean showingDescendants) {
 		this.isShowingDescendants = showingDescendants;
 	}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java
deleted file mode 100644
index a5b89405bf703b0bcf619a9226f58b371f4fb840..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package org.briarproject.android.forum;
-
-import org.briarproject.android.controller.handler.ResultHandler;
-import org.briarproject.api.UniqueId;
-import org.briarproject.api.forum.Forum;
-import org.briarproject.api.identity.Author;
-import org.briarproject.api.identity.AuthorFactory;
-import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.MessageId;
-
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import static org.briarproject.api.identity.Author.Status.UNVERIFIED;
-
-public class ForumTestControllerImpl implements ForumController {
-
-	@Inject
-	AuthorFactory authorFactory;
-
-	private static final Logger LOG =
-			Logger.getLogger(ForumControllerImpl.class.getName());
-
-	private final Author[] AUTHORS = {
-			authorFactory.createAuthor("Guðmundur", new byte[42]),
-			authorFactory.createAuthor("Jónas", new byte[42]),
-			authorFactory.createAuthor(
-					"Geir Þorsteinn Gísli Máni Halldórsson Guðjónsson Mogensen",
-					new byte[42]),
-			authorFactory.createAuthor("Baldur Friðrik", new byte[42]),
-			authorFactory.createAuthor("Anna Katrín", new byte[42]),
-			authorFactory.createAuthor("Þór", new byte[42]),
-			authorFactory.createAuthor("Anna Þorbjörg", new byte[42]),
-			authorFactory.createAuthor("Guðrún", new byte[42]),
-			authorFactory.createAuthor("Helga", new byte[42]),
-			authorFactory.createAuthor("Haraldur", new byte[42])
-	};
-
-	private final static String SAGA =
-			"Það er upphaf á sögu þessari að Hákon konungur " +
-					"Aðalsteinsfóstri réð fyrir Noregi og var þetta á ofanverðum " +
-					"hans dögum. Þorkell hét maður; hann var kallaður skerauki; " +
-					"hann bjó í Súrnadal og var hersir að nafnbót. Hann átti sér " +
-					"konu er Ísgerður hét og sonu þrjá barna; hét einn Ari, annar " +
-					"Gísli, þriðji Þorbjörn, hann var þeirra yngstur, og uxu allir " +
-					"upp heima þar. " +
-					"Maður er nefndur Ísi; hann bjó í firði er Fibuli heitir á " +
-					"Norðmæri; kona hans hét Ingigerður en Ingibjörg dóttir. Ari, " +
-					"sonur Þorkels Sýrdæls, biður hennar og var hún honum gefin " +
-					"með miklu fé. Kolur hét þræll er í brott fór með henni.";
-
-	private ForumEntry[] forumEntries;
-
-	@Inject
-	ForumTestControllerImpl() {
-
-	}
-
-	private void textRandomize(SecureRandom random, int[] i) {
-		for (int e = 0; e < forumEntries.length; e++) {
-			// select a random white-space for the cut-off
-			do {
-				i[e] = Math.abs(random.nextInt() % (SAGA.length()));
-			} while (SAGA.charAt(i[e]) != ' ');
-		}
-	}
-
-	private int levelRandomize(SecureRandom random, int[] l) {
-		int maxl = 0;
-		int lastl = 0;
-		l[0] = 0;
-		for (int e = 1; e < forumEntries.length; e++) {
-			// select random level 1-10
-			do {
-				l[e] = Math.abs(random.nextInt() % 10);
-			} while (l[e] > lastl + 1);
-			lastl = l[e];
-			if (lastl > maxl)
-				maxl = lastl;
-		}
-		return maxl;
-	}
-
-	@Override
-	public void loadForum(GroupId groupId,
-			ResultHandler<Boolean> resultHandler) {
-		SecureRandom random = new SecureRandom();
-		forumEntries = new ForumEntry[100];
-		// string cut off index
-		int[] i = new int[forumEntries.length];
-		// entry discussion level
-		int[] l = new int[forumEntries.length];
-
-		textRandomize(random, i);
-		int maxLevel;
-		// make sure we get a deep discussion
-		do {
-			maxLevel = levelRandomize(random, l);
-		} while (maxLevel < 6);
-		for (int e = 0; e < forumEntries.length; e++) {
-			int authorIndex = Math.abs(random.nextInt() % AUTHORS.length);
-			long timestamp =
-					System.currentTimeMillis() - Math.abs(random.nextInt());
-			byte[] b = new byte[UniqueId.LENGTH];
-			random.nextBytes(b);
-			forumEntries[e] =
-					new ForumEntry(new MessageId(b), SAGA.substring(0, i[e]),
-							l[e], timestamp, AUTHORS[authorIndex], UNVERIFIED);
-		}
-		LOG.info("forum entries: " + forumEntries.length);
-		resultHandler.onResult(true);
-	}
-
-	@Override
-	public Forum getForum() {
-		return null;
-	}
-
-	@Override
-	public List<ForumEntry> getForumEntries() {
-		return forumEntries == null ? null :
-				new ArrayList<>(Arrays.asList(forumEntries));
-	}
-
-	@Override
-	public void unsubscribe(ResultHandler<Boolean> resultHandler) {
-
-	}
-
-	@Override
-	public void entryRead(ForumEntry forumEntry) {
-
-	}
-
-	@Override
-	public void entriesRead(Collection<ForumEntry> messageIds) {
-
-	}
-
-	@Override
-	public void createPost(byte[] body) {
-
-	}
-
-	@Override
-	public void createPost(byte[] body, MessageId parentId) {
-
-	}
-
-	@Override
-	public void onActivityCreate() {
-
-	}
-
-	@Override
-	public void onActivityResume() {
-
-	}
-
-	@Override
-	public void onActivityPause() {
-
-	}
-
-	@Override
-	public void onActivityDestroy() {
-
-	}
-}
diff --git a/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java b/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..175ce5a162828b9c0c0cdfab00ac3add46ff12f9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java
@@ -0,0 +1,450 @@
+package org.briarproject.android.forum;
+
+import android.animation.Animator;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.android.util.NestedTreeList;
+import org.briarproject.android.view.AuthorView;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+public class NestedForumAdapter
+		extends RecyclerView.Adapter<NestedForumAdapter.NestedForumHolder> {
+
+	private static final int UNDEFINED = -1;
+
+	private final NestedTreeList<ForumEntry> forumEntries =
+			new NestedTreeList<>();
+	private final Map<ForumEntry, ValueAnimator> animatingEntries =
+			new HashMap<>();
+	// highlight not dependant on time
+	private ForumEntry replyEntry;
+	// temporary highlight
+	private ForumEntry addedEntry;
+	private final Context ctx;
+	private final OnNestedForumListener listener;
+	private final LinearLayoutManager layoutManager;
+
+	public NestedForumAdapter(Context ctx, OnNestedForumListener listener,
+			LinearLayoutManager layoutManager) {
+		this.ctx = ctx;
+		this.listener = listener;
+		this.layoutManager = layoutManager;
+	}
+
+	ForumEntry getReplyEntry() {
+		return replyEntry;
+	}
+
+	private void setForumEntryLevels() {
+		Stack<MessageId> idStack = new Stack<>();
+		for (ForumEntry forumEntry : forumEntries) {
+			if (forumEntry.getParentId() == null) {
+				idStack.clear();
+			} else if (idStack.isEmpty() ||
+					!idStack.contains(forumEntry.getParentId())) {
+				idStack.push(forumEntry.getParentId());
+			} else if (!forumEntry.getParentId().equals(idStack.peek())) {
+				do {
+					idStack.pop();
+				} while (!forumEntry.getParentId().equals(idStack.peek()));
+			}
+			forumEntry.setLevel(idStack.size());
+		}
+	}
+
+	void setEntries(List<ForumEntry> entries) {
+		forumEntries.clear();
+		forumEntries.addAll(entries);
+		setForumEntryLevels();
+		notifyItemRangeInserted(0, entries.size());
+	}
+
+	void addEntry(ForumEntry entry) {
+		boolean isShowingDescendants = false;
+		forumEntries.add(entry);
+		setForumEntryLevels();
+		if (entry.getLevel() > 0) {
+			// update parent and make sure descendants are visible
+			// Note that the parent's visibility is guaranteed (otherwise
+			// the reply button would not be visible)
+			for (int i = forumEntries.indexOf(entry) - 1; i >= 0; i--) {
+				ForumEntry higherEntry = forumEntries.get(i);
+				if (higherEntry.getLevel() < entry.getLevel()) {
+					// parent found
+					if (!higherEntry.isShowingDescendants()) {
+						isShowingDescendants = true;
+						showDescendants(higherEntry);
+					}
+					notifyItemChanged(getVisiblePos(higherEntry));
+					break;
+				}
+			}
+		}
+		if (!isShowingDescendants) {
+			int visiblePos = getVisiblePos(entry);
+			notifyItemInserted(visiblePos);
+		}
+		addedEntry = entry;
+	}
+
+	void scrollToEntry(ForumEntry entry) {
+		layoutManager
+				.scrollToPositionWithOffset(getVisiblePos(entry), 0);
+	}
+
+	private boolean hasDescendants(ForumEntry forumEntry) {
+		int i = forumEntries.indexOf(forumEntry);
+		if (i >= 0 && i < forumEntries.size() - 1) {
+			if (forumEntries.get(i + 1).getLevel() >
+					forumEntry.getLevel()) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean hasVisibleDescendants(ForumEntry forumEntry) {
+		int visiblePos = getVisiblePos(forumEntry);
+		int levelLimit = forumEntry.getLevel();
+		if (visiblePos + 1 < getItemCount()) {
+			ForumEntry entry = getVisibleEntry(visiblePos + 1);
+			if (entry == null || entry.getLevel() > levelLimit)
+				return true;
+		}
+		return false;
+	}
+
+	private int getReplyCount(ForumEntry entry) {
+		int counter = 0;
+		int pos = forumEntries.indexOf(entry);
+		if (pos >= 0) {
+			int ancestorLvl = forumEntries.get(pos).getLevel();
+			for (int i = pos + 1; i < forumEntries.size(); i++) {
+				int descendantLvl = forumEntries.get(i).getLevel();
+				if (descendantLvl <= ancestorLvl)
+					break;
+				if (descendantLvl == ancestorLvl + 1)
+					counter++;
+			}
+		}
+		return counter;
+	}
+
+	void setReplyEntryById(byte[] id) {
+		MessageId messageId = new MessageId(id);
+		for (ForumEntry entry : forumEntries) {
+			if (entry.getId().equals(messageId)) {
+				setReplyEntry(entry);
+				break;
+			}
+		}
+	}
+
+	void setReplyEntry(ForumEntry entry) {
+		if (replyEntry != null) {
+			notifyItemChanged(getVisiblePos(replyEntry));
+		}
+		replyEntry = entry;
+		if (replyEntry != null) {
+			notifyItemChanged(getVisiblePos(replyEntry));
+		}
+	}
+
+	private List<Integer> getSubTreeIndexes(int pos, int levelLimit) {
+		List<Integer> indexList = new ArrayList<>();
+
+		for (int i = pos + 1; i < getItemCount(); i++) {
+			ForumEntry entry = getVisibleEntry(i);
+			if (entry != null && entry.getLevel() > levelLimit) {
+				indexList.add(i);
+			} else {
+				break;
+			}
+		}
+		return indexList;
+	}
+
+	void showDescendants(ForumEntry forumEntry) {
+		forumEntry.setShowingDescendants(true);
+		int visiblePos = getVisiblePos(forumEntry);
+		List<Integer> indexList =
+				getSubTreeIndexes(visiblePos, forumEntry.getLevel());
+		if (!indexList.isEmpty()) {
+			if (indexList.size() == 1) {
+				notifyItemInserted(indexList.get(0));
+			} else {
+				notifyItemRangeInserted(indexList.get(0),
+						indexList.size());
+			}
+		}
+	}
+
+	void hideDescendants(ForumEntry forumEntry) {
+		int visiblePos = getVisiblePos(forumEntry);
+		List<Integer> indexList =
+				getSubTreeIndexes(visiblePos, forumEntry.getLevel());
+		if (!indexList.isEmpty()) {
+			if (Build.VERSION.SDK_INT >= 11) {
+				// stop animating children
+				for (int index : indexList) {
+					ValueAnimator anim =
+							animatingEntries.get(forumEntries.get(index));
+					if (anim != null && anim.isRunning()) {
+						anim.cancel();
+					}
+				}
+			}
+			if (indexList.size() == 1) {
+				notifyItemRemoved(indexList.get(0));
+			} else {
+				notifyItemRangeRemoved(indexList.get(0),
+						indexList.size());
+			}
+		}
+		forumEntry.setShowingDescendants(false);
+	}
+
+
+	@Nullable
+	ForumEntry getVisibleEntry(int position) {
+		int levelLimit = UNDEFINED;
+		for (ForumEntry forumEntry : forumEntries) {
+			if (levelLimit >= 0) {
+				if (forumEntry.getLevel() > levelLimit) {
+					continue;
+				}
+				levelLimit = UNDEFINED;
+			}
+			if (!forumEntry.isShowingDescendants()) {
+				levelLimit = forumEntry.getLevel();
+			}
+			if (position-- == 0) {
+				return forumEntry;
+			}
+		}
+		return null;
+	}
+
+	@TargetApi(11)
+	private void animateFadeOut(final NestedForumHolder ui,
+			final ForumEntry addedEntry) {
+		ui.setIsRecyclable(false);
+		ValueAnimator anim = new ValueAnimator();
+		animatingEntries.put(addedEntry, anim);
+		ColorDrawable viewColor = (ColorDrawable) ui.cell.getBackground();
+		anim.setIntValues(viewColor.getColor(), ContextCompat
+				.getColor(ctx, R.color.window_background));
+		anim.setEvaluator(new ArgbEvaluator());
+		anim.addListener(new Animator.AnimatorListener() {
+			@Override
+			public void onAnimationStart(Animator animation) {
+
+			}
+
+			@Override
+			public void onAnimationEnd(Animator animation) {
+				ui.setIsRecyclable(true);
+				animatingEntries.remove(addedEntry);
+			}
+
+			@Override
+			public void onAnimationCancel(Animator animation) {
+				ui.setIsRecyclable(true);
+				animatingEntries.remove(addedEntry);
+			}
+
+			@Override
+			public void onAnimationRepeat(Animator animation) {
+
+			}
+		});
+		anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+			@Override
+			public void onAnimationUpdate(ValueAnimator valueAnimator) {
+				ui.cell.setBackgroundColor(
+						(Integer) valueAnimator.getAnimatedValue());
+			}
+		});
+		anim.setDuration(5000);
+		anim.start();
+	}
+
+	@Override
+	public NestedForumHolder onCreateViewHolder(ViewGroup parent,
+			int viewType) {
+		View v = LayoutInflater.from(parent.getContext())
+				.inflate(R.layout.list_item_forum_post, parent, false);
+		return new NestedForumHolder(v);
+	}
+
+	@Override
+	public void onBindViewHolder(
+			final NestedForumHolder ui, final int position) {
+		final ForumEntry entry = getVisibleEntry(position);
+		if (entry == null) return;
+		listener.onEntryVisible(entry);
+
+		ui.textView.setText(StringUtils.trim(entry.getText()));
+
+		if (position == 0) {
+			ui.topDivider.setVisibility(View.INVISIBLE);
+		} else {
+			ui.topDivider.setVisibility(View.VISIBLE);
+		}
+
+		for (int i = 0; i < ui.lvls.length; i++) {
+			ui.lvls[i].setVisibility(i < entry.getLevel() ? VISIBLE : GONE);
+		}
+		if (entry.getLevel() > 5) {
+			ui.lvlText.setVisibility(VISIBLE);
+			ui.lvlText.setText("" + entry.getLevel());
+		} else {
+			ui.lvlText.setVisibility(GONE);
+		}
+		ui.author.setAuthor(entry.getAuthor());
+		ui.author.setDate(entry.getTimestamp());
+		ui.author.setAuthorStatus(entry.getStatus());
+
+		int replies = getReplyCount(entry);
+		if (replies == 0) {
+			ui.repliesText.setText("");
+		} else {
+			ui.repliesText.setText(
+					ctx.getResources()
+							.getQuantityString(R.plurals.message_replies,
+									replies, replies));
+		}
+
+		if (hasDescendants(entry)) {
+			ui.chevron.setVisibility(VISIBLE);
+			if (hasVisibleDescendants(entry)) {
+				ui.chevron.setSelected(false);
+			} else {
+				ui.chevron.setSelected(true);
+			}
+			ui.chevron.setOnClickListener(new View.OnClickListener() {
+				@Override
+				public void onClick(View v) {
+					ui.chevron.setSelected(!ui.chevron.isSelected());
+					if (ui.chevron.isSelected()) {
+						hideDescendants(entry);
+					} else {
+						showDescendants(entry);
+					}
+				}
+			});
+		} else {
+			ui.chevron.setVisibility(INVISIBLE);
+		}
+		if (entry.equals(replyEntry)) {
+			ui.cell.setBackgroundColor(ContextCompat
+					.getColor(ctx, R.color.forum_cell_highlight));
+		} else if (entry.equals(addedEntry)) {
+
+			ui.cell.setBackgroundColor(ContextCompat
+					.getColor(ctx, R.color.forum_cell_highlight));
+			if (Build.VERSION.SDK_INT >= 11) {
+				animateFadeOut(ui, addedEntry);
+			}
+			addedEntry = null;
+		} else {
+			ui.cell.setBackgroundColor(ContextCompat
+					.getColor(ctx, R.color.window_background));
+		}
+		ui.replyButton.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				listener.onReplyClick(entry);
+				scrollToEntry(entry);
+			}
+		});
+	}
+
+	private int getVisiblePos(ForumEntry sEntry) {
+		int visibleCounter = 0;
+		int levelLimit = UNDEFINED;
+		for (ForumEntry fEntry : forumEntries) {
+			if (levelLimit >= 0) {
+				if (fEntry.getLevel() > levelLimit) {
+					continue;
+				}
+				levelLimit = UNDEFINED;
+			}
+			if (sEntry != null && sEntry.equals(fEntry)) {
+				return visibleCounter;
+			} else if (!fEntry.isShowingDescendants()) {
+				levelLimit = fEntry.getLevel();
+			}
+			visibleCounter++;
+		}
+		return sEntry == null ? visibleCounter : NO_POSITION;
+	}
+
+	@Override
+	public int getItemCount() {
+		return getVisiblePos(null);
+	}
+
+	static class NestedForumHolder extends RecyclerView.ViewHolder {
+
+		final TextView textView, lvlText, repliesText;
+		final AuthorView author;
+		final View[] lvls;
+		final View chevron, replyButton;
+		final ViewGroup cell;
+		final View topDivider;
+
+		NestedForumHolder(View v) {
+			super(v);
+
+			textView = (TextView) v.findViewById(R.id.text);
+			lvlText = (TextView) v.findViewById(R.id.nested_line_text);
+			author = (AuthorView) v.findViewById(R.id.author);
+			repliesText = (TextView) v.findViewById(R.id.replies);
+			int[] nestedLineIds = {
+					R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
+					R.id.nested_line_4, R.id.nested_line_5
+			};
+			lvls = new View[nestedLineIds.length];
+			for (int i = 0; i < lvls.length; i++) {
+				lvls[i] = v.findViewById(nestedLineIds[i]);
+			}
+			chevron = v.findViewById(R.id.chevron);
+			replyButton = v.findViewById(R.id.btn_reply);
+			cell = (ViewGroup) v.findViewById(R.id.forum_cell);
+			topDivider = v.findViewById(R.id.top_divider);
+		}
+	}
+
+	interface OnNestedForumListener {
+		void onEntryVisible(ForumEntry forumEntry);
+
+		void onReplyClick(ForumEntry forumEntry);
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/util/NestedTreeList.java b/briar-android/src/org/briarproject/android/util/NestedTreeList.java
new file mode 100644
index 0000000000000000000000000000000000000000..23ba02298d1bc36b162a29bb5257b32de83bb60b
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/util/NestedTreeList.java
@@ -0,0 +1,49 @@
+package org.briarproject.android.util;
+
+import org.briarproject.api.clients.MessageTree;
+import org.briarproject.clients.MessageTreeImpl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/* This class is not thread safe */
+public class NestedTreeList<T extends MessageTree.MessageNode>
+		implements Iterable<T> {
+
+	private final MessageTree<T> tree = new MessageTreeImpl<>();
+	private List<T> depthFirstCollection = new ArrayList<>();
+
+	public void addAll(Collection<T> collection) {
+		tree.add(collection);
+		depthFirstCollection = new ArrayList<>(tree.depthFirstOrder());
+	}
+
+	public void add(T elem) {
+		tree.add(elem);
+		depthFirstCollection = new ArrayList<>(tree.depthFirstOrder());
+	}
+
+	public void clear() {
+		tree.clear();
+		depthFirstCollection.clear();
+	}
+
+	public T get(int index) {
+		return depthFirstCollection.get(index);
+	}
+
+	public int indexOf(T elem) {
+		return depthFirstCollection.indexOf(elem);
+	}
+
+	public int size() {
+		return depthFirstCollection.size();
+	}
+
+	@Override
+	public Iterator<T> iterator() {
+		return depthFirstCollection.iterator();
+	}
+}
diff --git a/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java b/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java
index e61b557beb392dbfedac9dd9620f0fd5ade0417b..92023f7f9185fdfb103eb37c3dc350af95d6d9b1 100644
--- a/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java
+++ b/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java
@@ -50,6 +50,22 @@ public class ForumActivityTest {
 			AUTHOR_1, AUTHOR_2, AUTHOR_3, AUTHOR_4, AUTHOR_5, AUTHOR_6
 	};
 
+	private final static MessageId[] AUTHOR_IDS = new MessageId[AUTHORS.length];
+
+	static {
+		for (int i = 0; i < AUTHOR_IDS.length; i++)
+			AUTHOR_IDS[i] = new MessageId(TestUtils.getRandomId());
+	}
+
+	private final static MessageId[] PARENT_AUTHOR_IDS = {
+			null,
+			AUTHOR_IDS[0],
+			AUTHOR_IDS[1],
+			AUTHOR_IDS[2],
+			AUTHOR_IDS[0],
+			null
+	};
+
 	/*
 		1
 		-> 2
@@ -64,7 +80,7 @@ public class ForumActivityTest {
 
 	private TestForumActivity forumActivity;
 	@Captor
-	private ArgumentCaptor<UiResultHandler<Boolean>> rc;
+	private ArgumentCaptor<UiResultHandler<List<ForumEntry>>> rc;
 
 	@Before
 	public void setUp() {
@@ -82,9 +98,11 @@ public class ForumActivityTest {
 			AuthorId authorId = new AuthorId(TestUtils.getRandomId());
 			byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
 			Author author = new Author(authorId, AUTHORS[i], publicKey);
-			forumEntries[i] = new ForumEntry(
-					new MessageId(TestUtils.getRandomId()), AUTHORS[i],
-					LEVELS[i], System.currentTimeMillis(), author, UNKNOWN);
+			forumEntries[i] =
+					new ForumEntry(AUTHOR_IDS[i], PARENT_AUTHOR_IDS[i],
+							AUTHORS[i], System.currentTimeMillis(), author,
+							UNKNOWN);
+			forumEntries[i].setLevel(LEVELS[i]);
 		}
 		return new ArrayList<>(Arrays.asList(forumEntries));
 	}
@@ -93,13 +111,10 @@ public class ForumActivityTest {
 	public void testNestedEntries() {
 		ForumController mc = forumActivity.getController();
 		List<ForumEntry> dummyData = getDummyData();
-		Mockito.when(mc.getForumEntries()).thenReturn(dummyData);
-		// Verify that the forum load is called once
 		verify(mc, times(1))
 				.loadForum(Mockito.any(GroupId.class), rc.capture());
-		rc.getValue().onResult(true);
-		verify(mc, times(1)).getForumEntries();
-		ForumActivity.ForumAdapter adapter = forumActivity.getAdapter();
+		rc.getValue().onResult(dummyData);
+		NestedForumAdapter adapter = forumActivity.getAdapter();
 		Assert.assertNotNull(adapter);
 		// Cascade close
 		assertEquals(6, adapter.getItemCount());
diff --git a/briar-android/test/java/org/briarproject/android/forum/TestForumActivity.java b/briar-android/test/java/org/briarproject/android/forum/TestForumActivity.java
index 43dc8998567c3098157617530bb90771df729a1f..d38e5a31f6dcf6adddb943297aad79fe40cdba24 100644
--- a/briar-android/test/java/org/briarproject/android/forum/TestForumActivity.java
+++ b/briar-android/test/java/org/briarproject/android/forum/TestForumActivity.java
@@ -15,7 +15,7 @@ public class TestForumActivity extends ForumActivity {
 		return forumController;
 	}
 
-	public ForumAdapter getAdapter() {
+	public NestedForumAdapter getAdapter() {
 		return forumAdapter;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
index ef08dc022fa32d758bca4d0280b8a6a541fa22f7..b98d23448ce6dd4ec3c43b164082b0e00e7c3d3a 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
@@ -1,12 +1,10 @@
 package org.briarproject.api.forum;
 
-import org.briarproject.api.clients.MessageTree;
 import org.briarproject.api.clients.PostHeader;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.MessageId;
 
-public class ForumPostHeader extends PostHeader
-		implements MessageTree.MessageNode {
+public class ForumPostHeader extends PostHeader {
 
 	public ForumPostHeader(MessageId id, MessageId parentId, long timestamp,
 			Author author, Author.Status authorStatus, boolean read) {
diff --git a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
index 58e5c65a47ebd6f0498929f184684bbcfb7c8ffb..2c6eb1344a144d2f1955faba483e6c54e0d7f1a1 100644
--- a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
+++ b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
@@ -26,13 +26,13 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 	};
 
 	@Override
-	public void clear() {
+	public synchronized void clear() {
 		roots.clear();
 		nodeMap.clear();
 	}
 
 	@Override
-	public void add(Collection<T> nodes) {
+	public synchronized void add(Collection<T> nodes) {
 		// add all nodes to the node map
 		for (T node : nodes) {
 			nodeMap.put(node.getId(), new ArrayList<T>());
@@ -45,7 +45,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 	}
 
 	@Override
-	public void add(T node) {
+	public synchronized void add(T node) {
 		add(Collections.singletonList(node));
 	}
 
@@ -85,7 +85,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 	}
 
 	@Override
-	public void setComparator(Comparator<T> comparator) {
+	public synchronized void setComparator(Comparator<T> comparator) {
 		this.comparator = comparator;
 		// Sort all lists with the new comparator
 		Collections.sort(roots, comparator);
@@ -95,7 +95,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 	}
 
 	@Override
-	public Collection<T> depthFirstOrder() {
+	public synchronized Collection<T> depthFirstOrder() {
 		List<T> orderedList = new ArrayList<T>();
 		for (T root : roots) {
 			traverse(orderedList, root);