From dbdf567d4e39c6be0a42e6c22d2d84df06f7325a Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 2 Aug 2016 16:48:16 +0100
Subject: [PATCH] Overhaul notifications to fix various bugs.

Fixes #539, #564 and #568.
---
 briar-android/res/values/strings.xml          |   6 +-
 .../AndroidNotificationManagerImpl.java       | 348 ++++++++++++++----
 .../api/AndroidNotificationManager.java       |  27 +-
 .../android/blogs/BlogControllerImpl.java     |   6 +
 .../android/blogs/BlogFragment.java           |   1 +
 .../android/blogs/FeedControllerImpl.java     |  11 +-
 .../android/blogs/WriteBlogPostActivity.java  |  21 +-
 .../android/contact/ContactListFragment.java  |   6 +
 .../android/forum/ForumListFragment.java      |  13 +-
 9 files changed, 341 insertions(+), 98 deletions(-)

diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 496fd0c598..d326d4729b 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -134,8 +134,10 @@
 	<string name="introduction_response_accepted_received">%1$s accepted the introduction to %2$s.</string>
 	<string name="introduction_response_declined_received">%1$s declined the introduction to %2$s.</string>
 	<string name="introduction_response_declined_received_by_introducee">%1$s says that %2$s declined the introduction.</string>
-	<string name="introduction_success_title">Introduced contact was added</string>
-	<string name="introduction_success_text">You have been introduced to %1$s.</string>
+	<plurals name="introduction_notification_text">
+		<item quantity="one">New contact added.</item>
+		<item quantity="other">%d new contacts added.</item>
+	</plurals>
 
 	<!-- Forums -->
 	<string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string>
diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
index dc53745b98..9ba3f6792f 100644
--- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
@@ -2,8 +2,11 @@ package org.briarproject.android;
 
 import android.app.Application;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Build;
 import android.support.v4.app.NotificationCompat;
@@ -14,7 +17,6 @@ import org.briarproject.android.api.AndroidExecutor;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.contact.ConversationActivity;
 import org.briarproject.android.forum.ForumActivity;
-import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
@@ -59,20 +61,38 @@ import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
 import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
 import static java.util.logging.Level.WARNING;
 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.fragment.SettingsFragment.PREF_NOTIFY_BLOG;
 import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
 
 class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		Service, EventListener {
 
+	// Notification IDs
 	private static final int PRIVATE_MESSAGE_NOTIFICATION_ID = 3;
 	private static final int FORUM_POST_NOTIFICATION_ID = 4;
 	private static final int BLOG_POST_NOTIFICATION_ID = 5;
 	private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 6;
+
+	// Content URIs to differentiate between pending intents
 	private static final String CONTACT_URI =
 			"content://org.briarproject/contact";
 	private static final String FORUM_URI =
 			"content://org.briarproject/forum";
+	private static final String BLOG_URI =
+			"content://org.briarproject/blog";
+
+	// Actions for intents that are broadcast when notifications are dismissed
+	private static final String CLEAR_PRIVATE_MESSAGE_ACTION =
+			"org.briarproject.briar.CLEAR_PRIVATE_MESSAGE_NOTIFICATION";
+	private static final String CLEAR_FORUM_ACTION =
+			"org.briarproject.briar.CLEAR_FORUM_NOTIFICATION";
+	private static final String CLEAR_BLOG_ACTION =
+			"org.briarproject.briar.CLEAR_BLOG_NOTIFICATION";
+	private static final String CLEAR_INTRODUCTION_ACTION =
+			"org.briarproject.briar.CLEAR_INTRODUCTION_NOTIFICATION";
 
 	private static final Logger LOG =
 			Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -82,16 +102,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	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
+	// The following must only be accessed on the AndroidExecutor thread
 	private final Map<GroupId, Integer> contactCounts = new HashMap<>();
 	private final Map<GroupId, Integer> forumCounts = new HashMap<>();
-	private final AtomicBoolean used = new AtomicBoolean(false);
-
+	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 GroupId visibleGroup = null;
-	private boolean blogBlocked = false;
+	private GroupId blockedGroup = null;
+	private boolean blockContacts = false, blockForums = false;
+	private boolean blockBlogs = false, blockIntroductions = false;
 
 	private volatile Settings settings = new Settings();
 
@@ -109,21 +132,43 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	@Override
 	public void startService() throws ServiceException {
 		if (used.getAndSet(true)) throw new IllegalStateException();
+		// Load settings
 		try {
 			settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
 		} catch (DbException e) {
 			throw new ServiceException(e);
 		}
+		// Register a broadcast receiver for notifications being dismissed
+		Future<Void> f = androidExecutor.submit(new Callable<Void>() {
+			@Override
+			public Void call() {
+				IntentFilter filter = new IntentFilter();
+				filter.addAction(CLEAR_PRIVATE_MESSAGE_ACTION);
+				filter.addAction(CLEAR_FORUM_ACTION);
+				filter.addAction(CLEAR_BLOG_ACTION);
+				filter.addAction(CLEAR_INTRODUCTION_ACTION);
+				appContext.registerReceiver(receiver, filter);
+				return null;
+			}
+		});
+		try {
+			f.get();
+		} catch (InterruptedException | ExecutionException e) {
+			throw new ServiceException(e);
+		}
 	}
 
 	@Override
 	public void stopService() throws ServiceException {
+		// Clear all notifications and unregister the broadcast receiver
 		Future<Void> f = androidExecutor.submit(new Callable<Void>() {
 			@Override
 			public Void call() {
 				clearPrivateMessageNotification();
 				clearForumPostNotification();
+				clearBlogPostNotification();
 				clearIntroductionSuccessNotification();
+				appContext.unregisterReceiver(receiver);
 				return null;
 			}
 		});
@@ -135,18 +180,31 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	}
 
 	private void clearPrivateMessageNotification() {
+		contactCounts.clear();
+		contactTotal = 0;
 		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
 		NotificationManager nm = (NotificationManager) o;
 		nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
 	}
 
 	private void clearForumPostNotification() {
+		forumCounts.clear();
+		forumTotal = 0;
 		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
 		NotificationManager nm = (NotificationManager) o;
 		nm.cancel(FORUM_POST_NOTIFICATION_ID);
 	}
 
+	private void clearBlogPostNotification() {
+		blogCounts.clear();
+		blogTotal = 0;
+		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+		NotificationManager nm = (NotificationManager) o;
+		nm.cancel(BLOG_POST_NOTIFICATION_ID);
+	}
+
 	private void clearIntroductionSuccessNotification() {
+		introductionTotal = 0;
 		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
 		NotificationManager nm = (NotificationManager) o;
 		nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
@@ -158,29 +216,28 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
 			if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
 		} else if (e instanceof PrivateMessageReceivedEvent) {
-			PrivateMessageReceivedEvent m = (PrivateMessageReceivedEvent) e;
-			showPrivateMessageNotification(m.getGroupId());
+			PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
+			showPrivateMessageNotification(p.getGroupId());
 		} else if (e instanceof ForumPostReceivedEvent) {
-			ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
-			showForumPostNotification(m.getGroupId());
+			ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
+			showForumPostNotification(f.getGroupId());
 		} else if (e instanceof BlogPostAddedEvent) {
-			BlogPostAddedEvent be = (BlogPostAddedEvent) e;
-			showBlogPostNotification(be.getGroupId());
+			BlogPostAddedEvent b = (BlogPostAddedEvent) e;
+			showBlogPostNotification(b.getGroupId());
 		} else if (e instanceof IntroductionRequestReceivedEvent) {
 			ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
 			showNotificationForPrivateConversation(c);
 		} else if (e instanceof IntroductionResponseReceivedEvent) {
 			ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId();
 			showNotificationForPrivateConversation(c);
-		} else if (e instanceof IntroductionSucceededEvent) {
-			Contact c = ((IntroductionSucceededEvent) e).getContact();
-			showIntroductionSucceededNotification(c);
 		} else if (e instanceof InvitationReceivedEvent) {
 			ContactId c = ((InvitationReceivedEvent) e).getContactId();
 			showNotificationForPrivateConversation(c);
 		} else if (e instanceof InvitationResponseReceivedEvent) {
 			ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();
 			showNotificationForPrivateConversation(c);
+		} else if (e instanceof IntroductionSucceededEvent) {
+			showIntroductionNotification();
 		}
 	}
 
@@ -198,17 +255,17 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		});
 	}
 
-	@Override
-	public void showPrivateMessageNotification(final GroupId g) {
+	private void showPrivateMessageNotification(final GroupId g) {
 		androidExecutor.execute(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);
 				contactTotal++;
-				if (!g.equals(visibleGroup))
-					updatePrivateMessageNotification();
+				updatePrivateMessageNotification();
 			}
 		});
 	}
@@ -221,7 +278,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				Integer count = contactCounts.remove(g);
 				if (count == null) return; // Already cleared
 				contactTotal -= count;
-				// FIXME: If the notification isn't showing, this may show it
 				updatePrivateMessageNotification();
 			}
 		});
@@ -245,7 +301,13 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			b.setDefaults(getDefaults());
 			b.setOnlyAlertOnce(true);
 			b.setAutoCancel(true);
+			// Clear the counters if the notification is dismissed
+			Intent clear = new Intent(CLEAR_PRIVATE_MESSAGE_ACTION);
+			PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
+					clear, 0);
+			b.setDeleteIntent(delete);
 			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());
@@ -257,9 +319,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				t.addNextIntent(i);
 				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 			} else {
+				// Touching the notification shows the contact list
 				Intent i = new Intent(appContext, NavDrawerActivity.class);
-				i.putExtra(NavDrawerActivity.INTENT_CONTACTS, true);
+				i.putExtra(INTENT_CONTACTS, true);
 				i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
+				i.setData(Uri.parse(CONTACT_URI));
 				TaskStackBuilder t = TaskStackBuilder.create(appContext);
 				t.addParentStack(NavDrawerActivity.class);
 				t.addNextIntent(i);
@@ -287,16 +351,27 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	}
 
 	@Override
-	public void showForumPostNotification(final GroupId g) {
+	public void clearAllContactNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
+				clearPrivateMessageNotification();
+				clearIntroductionSuccessNotification();
+			}
+		});
+	}
+
+	private void showForumPostNotification(final GroupId g) {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				if (blockForums) return;
+				if (g.equals(blockedGroup)) return;
 				Integer count = forumCounts.get(g);
 				if (count == null) forumCounts.put(g, 1);
 				else forumCounts.put(g, count + 1);
 				forumTotal++;
-				if (!g.equals(visibleGroup))
-					updateForumPostNotification();
+				updateForumPostNotification();
 			}
 		});
 	}
@@ -309,7 +384,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				Integer count = forumCounts.remove(g);
 				if (count == null) return; // Already cleared
 				forumTotal -= count;
-				// FIXME: If the notification isn't showing, this may show it
 				updateForumPostNotification();
 			}
 		});
@@ -332,7 +406,13 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			b.setDefaults(getDefaults());
 			b.setOnlyAlertOnce(true);
 			b.setAutoCancel(true);
+			// Clear the counters if the notification is dismissed
+			Intent clear = new Intent(CLEAR_FORUM_ACTION);
+			PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
+					clear, 0);
+			b.setDeleteIntent(delete);
 			if (forumCounts.size() == 1) {
+				// Touching the notification shows the relevant forum
 				Intent i = new Intent(appContext, ForumActivity.class);
 				GroupId g = forumCounts.keySet().iterator().next();
 				i.putExtra(GROUP_ID, g.getBytes());
@@ -344,9 +424,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				t.addNextIntent(i);
 				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 			} else {
+				// Touching the notification shows the forum list
 				Intent i = new Intent(appContext, NavDrawerActivity.class);
-				i.putExtra(NavDrawerActivity.INTENT_FORUMS, true);
+				i.putExtra(INTENT_FORUMS, true);
 				i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
+				i.setData(Uri.parse(FORUM_URI));
 				TaskStackBuilder t = TaskStackBuilder.create(appContext);
 				t.addParentStack(NavDrawerActivity.class);
 				t.addNextIntent(i);
@@ -363,28 +445,47 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	}
 
 	@Override
-	public void showBlogPostNotification(final GroupId g) {
+	public void clearAllForumPostNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				if (!blogBlocked) {
-					blogTotal++;
-					updateBlogPostNotification();
-				}
+				clearForumPostNotification();
+			}
+		});
+	}
+
+	private void showBlogPostNotification(final GroupId g) {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				if (blockBlogs) return;
+				if (g.equals(blockedGroup)) return;
+				Integer count = blogCounts.get(g);
+				if (count == null) blogCounts.put(g, 1);
+				else blogCounts.put(g, count + 1);
+				blogTotal++;
+				updateBlogPostNotification();
 			}
 		});
 	}
 
 	@Override
-	public void clearBlogPostNotification() {
-		blogTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(BLOG_POST_NOTIFICATION_ID);
+	public void clearBlogPostNotification(final GroupId g) {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				Integer count = blogCounts.remove(g);
+				if (count == null) return; // Already cleared
+				blogTotal -= count;
+				updateBlogPostNotification();
+			}
+		});
 	}
 
 	private void updateBlogPostNotification() {
-		if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
+		if (blogTotal == 0) {
+			clearBlogPostNotification();
+		} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
 			NotificationCompat.Builder b =
 					new NotificationCompat.Builder(appContext);
 			b.setSmallIcon(R.drawable.message_notification_icon);
@@ -398,15 +499,20 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			b.setDefaults(getDefaults());
 			b.setOnlyAlertOnce(true);
 			b.setAutoCancel(true);
-
+			// Clear the counters if the notification is dismissed
+			Intent clear = new Intent(CLEAR_BLOG_ACTION);
+			PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
+					clear, 0);
+			b.setDeleteIntent(delete);
+			// Touching the notification shows the combined blog feed
 			Intent i = new Intent(appContext, NavDrawerActivity.class);
-			i.putExtra(NavDrawerActivity.INTENT_BLOGS, true);
+			i.putExtra(INTENT_BLOGS, true);
 			i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
+			i.setData(Uri.parse(BLOG_URI));
 			TaskStackBuilder t = TaskStackBuilder.create(appContext);
 			t.addParentStack(NavDrawerActivity.class);
 			t.addNextIntent(i);
 			b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
-
 			if (Build.VERSION.SDK_INT >= 21) {
 				b.setCategory(CATEGORY_SOCIAL);
 				b.setVisibility(VISIBILITY_SECRET);
@@ -417,12 +523,70 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		}
 	}
 
+	@Override
+	public void clearAllBlogPostNotifications() {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				clearBlogPostNotification();
+			}
+		});
+	}
+
+	private void showIntroductionNotification() {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				if (blockIntroductions) return;
+				introductionTotal++;
+				updateIntroductionNotification();
+			}
+		});
+	}
+
+	private void updateIntroductionNotification() {
+		NotificationCompat.Builder b =
+				new NotificationCompat.Builder(appContext);
+		b.setSmallIcon(R.drawable.introduction_notification);
+		b.setContentTitle(appContext.getText(R.string.app_name));
+		b.setContentText(appContext.getResources().getQuantityString(
+				R.plurals.introduction_notification_text, introductionTotal,
+				introductionTotal));
+		String ringtoneUri = settings.get("notifyRingtoneUri");
+		if (!StringUtils.isNullOrEmpty(ringtoneUri))
+			b.setSound(Uri.parse(ringtoneUri));
+		b.setDefaults(getDefaults());
+		b.setOnlyAlertOnce(true);
+		b.setAutoCancel(true);
+		// Clear the counter if the notification is dismissed
+		Intent clear = new Intent(CLEAR_INTRODUCTION_ACTION);
+		PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
+				clear, 0);
+		b.setDeleteIntent(delete);
+		// Touching the notification shows the contact list
+		Intent i = new Intent(appContext, NavDrawerActivity.class);
+		i.putExtra(INTENT_CONTACTS, true);
+		i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
+		i.setData(Uri.parse(CONTACT_URI));
+		TaskStackBuilder t = TaskStackBuilder.create(appContext);
+		t.addParentStack(NavDrawerActivity.class);
+		t.addNextIntent(i);
+		b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
+		if (Build.VERSION.SDK_INT >= 21) {
+			b.setCategory(CATEGORY_MESSAGE);
+			b.setVisibility(VISIBILITY_SECRET);
+		}
+		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+		NotificationManager nm = (NotificationManager) o;
+		nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
+	}
+
 	@Override
 	public void blockNotification(final GroupId g) {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				visibleGroup = g;
+				blockedGroup = g;
 			}
 		});
 	}
@@ -432,75 +596,107 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				if (g.equals(visibleGroup)) visibleGroup = null;
+				if (g.equals(blockedGroup)) blockedGroup = null;
 			}
 		});
 	}
 
 	@Override
-	public void blockBlogNotification() {
+	public void blockAllContactNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				blogBlocked = true;
+				blockContacts = true;
+				blockIntroductions = true;
 			}
 		});
 	}
 
 	@Override
-	public void unblockBlogNotification() {
+	public void unblockAllContactNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				blogBlocked = false;
+				blockContacts = false;
+				blockIntroductions = false;
 			}
 		});
 	}
 
-	private void showNotificationForPrivateConversation(final ContactId c) {
+	@Override
+	public void blockAllForumPostNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				try {
-					GroupId group = messagingManager.getConversationId(c);
-					showPrivateMessageNotification(group);
-				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-				}
+				blockForums = true;
 			}
 		});
 	}
 
-	private void showIntroductionSucceededNotification(final Contact c) {
+	@Override
+	public void unblockAllForumPostNotifications() {
 		androidExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				NotificationCompat.Builder b =
-						new NotificationCompat.Builder(appContext);
-				b.setSmallIcon(R.drawable.introduction_notification);
-
-				b.setContentTitle(appContext
-						.getString(R.string.introduction_success_title));
-				b.setContentText(appContext
-						.getString(R.string.introduction_success_text,
-								c.getAuthor().getName()));
-				b.setDefaults(getDefaults());
-				b.setAutoCancel(true);
+				blockForums = false;
+			}
+		});
+	}
 
-				Intent i = new Intent(appContext, NavDrawerActivity.class);
-				i.putExtra(NavDrawerActivity.INTENT_CONTACTS, true);
-				i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
-				TaskStackBuilder t = TaskStackBuilder.create(appContext);
-				t.addParentStack(NavDrawerActivity.class);
-				t.addNextIntent(i);
-				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
+	@Override
+	public void blockAllBlogPostNotifications() {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				blockBlogs = true;
+			}
+		});
+	}
+
+	@Override
+	public void unblockAllBlogPostNotifications() {
+		androidExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				blockBlogs = false;
+			}
+		});
+	}
 
-				Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-				NotificationManager nm = (NotificationManager) o;
-				nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
+	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
+		public void onReceive(Context context, Intent intent) {
+			final String action = intent.getAction();
+			androidExecutor.execute(new Runnable() {
+				@Override
+				public void run() {
+					if (CLEAR_PRIVATE_MESSAGE_ACTION.equals(action)) {
+						clearPrivateMessageNotification();
+					} else if (CLEAR_FORUM_ACTION.equals(action)) {
+						clearForumPostNotification();
+					} else if (CLEAR_BLOG_ACTION.equals(action)) {
+						clearBlogPostNotification();
+					} else if (CLEAR_INTRODUCTION_ACTION.equals(action)) {
+						clearIntroductionSuccessNotification();
+					}
+				}
+			});
+		}
+	}
 }
diff --git a/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java b/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
index 776ed723af..217d8937d6 100644
--- a/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
+++ b/briar-android/src/org/briarproject/android/api/AndroidNotificationManager.java
@@ -2,26 +2,37 @@ package org.briarproject.android.api;
 
 import org.briarproject.api.sync.GroupId;
 
-/** Manages notifications for private messages and forum posts. */
+/**
+ * Manages notifications for private messages, forum posts, blog posts and
+ * introductions.
+ */
 public interface AndroidNotificationManager {
 
-	void showPrivateMessageNotification(GroupId g);
-
 	void clearPrivateMessageNotification(GroupId g);
 
-	void showForumPostNotification(GroupId g);
+	void clearAllContactNotifications();
 
 	void clearForumPostNotification(GroupId g);
 
-	void showBlogPostNotification(GroupId g);
+	void clearAllForumPostNotifications();
+
+	void clearBlogPostNotification(GroupId g);
 
-	void clearBlogPostNotification();
+	void clearAllBlogPostNotifications();
 
 	void blockNotification(GroupId g);
 
 	void unblockNotification(GroupId g);
 
-	void blockBlogNotification();
+	void blockAllContactNotifications();
+
+	void unblockAllContactNotifications();
+
+	void blockAllForumPostNotifications();
+
+	void unblockAllForumPostNotifications();
+
+	void blockAllBlogPostNotifications();
 
-	void unblockBlogNotification();
+	void unblockAllBlogPostNotifications();
 }
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
index 2d20ab1e40..0ad72953e8 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
@@ -2,6 +2,7 @@ package org.briarproject.android.blogs;
 
 import android.app.Activity;
 
+import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.DbControllerImpl;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.api.blogs.Blog;
@@ -38,6 +39,8 @@ public class BlogControllerImpl extends DbControllerImpl
 	protected Activity activity;
 	@Inject
 	protected EventBus eventBus;
+	@Inject
+	protected AndroidNotificationManager notificationManager;
 
 	@Inject
 	protected volatile BlogManager blogManager;
@@ -69,11 +72,14 @@ public class BlogControllerImpl extends DbControllerImpl
 
 	@Override
 	public void onActivityResume() {
+		notificationManager.blockNotification(groupId);
+		notificationManager.clearBlogPostNotification(groupId);
 		eventBus.addListener(this);
 	}
 
 	@Override
 	public void onActivityPause() {
+		notificationManager.unblockNotification(groupId);
 		eventBus.removeListener(this);
 	}
 
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
index 92236d4b4c..0074fa0d09 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -121,6 +121,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 	@Override
 	public void injectFragment(ActivityComponent component) {
 		component.inject(this);
+		blogController.setGroupId(groupId);
 	}
 
 	@Override
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java
index e02022ac1c..7c093598b1 100644
--- a/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java
@@ -33,12 +33,13 @@ public class FeedControllerImpl extends DbControllerImpl
 	@SuppressWarnings("WeakerAccess")
 	@Inject
 	AndroidNotificationManager notificationManager;
+	@Inject
+	protected EventBus eventBus;
+
 	@Inject
 	protected volatile BlogManager blogManager;
 	@Inject
 	protected volatile IdentityManager identityManager;
-	@Inject
-	protected volatile EventBus eventBus;
 
 	private volatile OnBlogPostAddedListener listener;
 
@@ -48,14 +49,14 @@ public class FeedControllerImpl extends DbControllerImpl
 
 	@Override
 	public void onResume() {
-		notificationManager.blockBlogNotification();
-		notificationManager.clearBlogPostNotification();
+		notificationManager.blockAllBlogPostNotifications();
+		notificationManager.clearAllBlogPostNotifications();
 		eventBus.addListener(this);
 	}
 
 	@Override
 	public void onPause() {
-		notificationManager.unblockBlogNotification();
+		notificationManager.unblockAllBlogPostNotifications();
 		eventBus.removeListener(this);
 	}
 
diff --git a/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java b/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java
index 7b5e2caa9c..4e6d24312c 100644
--- a/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java
+++ b/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java
@@ -18,6 +18,7 @@ import android.widget.TextView.OnEditorActionListener;
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.BriarActivity;
+import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.blogs.BlogManager;
 import org.briarproject.api.blogs.BlogPost;
@@ -47,6 +48,9 @@ public class WriteBlogPostActivity extends BriarActivity
 			Logger.getLogger(WriteBlogPostActivity.class.getName());
 	private static final String contentType = "text/plain";
 
+	@Inject
+	protected AndroidNotificationManager notificationManager;
+
 	private TextInputEditText titleInput;
 	private EditText bodyInput;
 	private Button publishButton;
@@ -69,13 +73,8 @@ public class WriteBlogPostActivity extends BriarActivity
 		byte[] b = i.getByteArrayExtra(GROUP_ID);
 		if (b == null) throw new IllegalStateException("No Group in intent.");
 		groupId = new GroupId(b);
-//		String blogName = i.getStringExtra(BLOG_NAME);
-//		if (blogName != null) setTitle(blogName);
 
 		setContentView(R.layout.activity_write_blog_post);
-//		String title =
-//				getTitle() + ": " + getString(R.string.blogs_write_blog_post);
-//		setTitle(title);
 
 		TextInputLayout titleLayout =
 				(TextInputLayout) findViewById(R.id.titleLayout);
@@ -116,6 +115,18 @@ public class WriteBlogPostActivity extends BriarActivity
 		progressBar = (ProgressBar) findViewById(R.id.progressBar);
 	}
 
+	@Override
+	public void onPause() {
+		super.onPause();
+		notificationManager.unblockNotification(groupId);
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		notificationManager.blockNotification(groupId);
+	}
+
 	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
 		switch (item.getItemId()) {
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index afda3f0cf9..bead9c4db6 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -17,6 +17,7 @@ import android.view.ViewGroup;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.keyagreement.KeyAgreementActivity;
 import org.briarproject.android.util.BriarRecyclerView;
@@ -75,6 +76,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 	ConnectionRegistry connectionRegistry;
 	@Inject
 	protected EventBus eventBus;
+	@Inject
+	protected AndroidNotificationManager notificationManager;
 
 	private ContactListAdapter adapter = null;
 	private BriarRecyclerView list = null;
@@ -184,6 +187,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 	@Override
 	public void onResume() {
 		super.onResume();
+		notificationManager.blockAllContactNotifications();
+		notificationManager.clearAllContactNotifications();
 		eventBus.addListener(this);
 		loadContacts();
 		list.startPeriodicUpdate();
@@ -193,6 +198,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 	public void onPause() {
 		super.onPause();
 		eventBus.removeListener(this);
+		notificationManager.unblockAllContactNotifications();
 		adapter.clear();
 		list.showProgressBar();
 		list.stopPeriodicUpdate();
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index 0f33788523..11c9bd2930 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -15,6 +15,7 @@ import android.view.ViewGroup;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.fragment.BaseEventFragment;
 import org.briarproject.android.sharing.InvitationsForumActivity;
 import org.briarproject.android.util.BriarRecyclerView;
@@ -55,9 +56,14 @@ public class ForumListFragment extends BaseEventFragment implements
 	private ForumListAdapter adapter;
 	private Snackbar snackbar;
 
+	@Inject
+	protected AndroidNotificationManager notificationManager;
+
 	// Fields that are accessed from background threads must be volatile
-	@Inject protected volatile ForumManager forumManager;
-	@Inject protected volatile ForumSharingManager forumSharingManager;
+	@Inject
+	protected volatile ForumManager forumManager;
+	@Inject
+	protected volatile ForumSharingManager forumSharingManager;
 
 	public static ForumListFragment newInstance() {
 
@@ -109,6 +115,8 @@ public class ForumListFragment extends BaseEventFragment implements
 	public void onResume() {
 		super.onResume();
 
+		notificationManager.blockAllForumPostNotifications();
+		notificationManager.clearAllForumPostNotifications();
 		loadForumHeaders();
 		loadAvailableForums();
 		list.startPeriodicUpdate();
@@ -118,6 +126,7 @@ public class ForumListFragment extends BaseEventFragment implements
 	public void onPause() {
 		super.onPause();
 
+		notificationManager.unblockAllForumPostNotifications();
 		adapter.clear();
 		list.showProgressBar();
 		list.stopPeriodicUpdate();
-- 
GitLab