diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index f71e67979..229cf4924 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -76,10 +76,8 @@
Add
Cancel
Post sent
- New private message
- Touch to show.
- New forum post
- Touch to show.
+ New private message.
+ New forum post.
Settings
Activate Bluetooth while signed in
Briar uses Bluetooth to communicate with nearby contacts
diff --git a/briar-android/src/org/briarproject/android/AndroidModule.java b/briar-android/src/org/briarproject/android/AndroidModule.java
index 78c7a3a57..3f871d2c9 100644
--- a/briar-android/src/org/briarproject/android/AndroidModule.java
+++ b/briar-android/src/org/briarproject/android/AndroidModule.java
@@ -14,6 +14,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Singleton;
import org.briarproject.api.android.AndroidExecutor;
+import org.briarproject.api.android.AndroidNotificationManager;
import org.briarproject.api.android.DatabaseUiExecutor;
import org.briarproject.api.android.ReferenceManager;
import org.briarproject.api.db.DatabaseConfig;
@@ -57,9 +58,12 @@ public class AndroidModule extends AbstractModule {
}
protected void configure() {
- bind(AndroidExecutor.class).to(AndroidExecutorImpl.class);
- bind(ReferenceManager.class).to(
- ReferenceManagerImpl.class).in(Singleton.class);
+ bind(AndroidExecutor.class).to(AndroidExecutorImpl.class).in(
+ Singleton.class);
+ bind(AndroidNotificationManager.class).to(
+ AndroidNotificationManagerImpl.class).in(Singleton.class);
+ bind(ReferenceManager.class).to(ReferenceManagerImpl.class).in(
+ Singleton.class);
bind(UiCallback.class).toInstance(uiCallback);
}
diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
new file mode 100644
index 000000000..51dcea4d1
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
@@ -0,0 +1,170 @@
+package org.briarproject.android;
+
+import static android.app.Notification.DEFAULT_ALL;
+import static android.content.Context.NOTIFICATION_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.briarproject.R;
+import org.briarproject.android.contact.ContactListActivity;
+import org.briarproject.android.contact.ConversationActivity;
+import org.briarproject.android.groups.GroupActivity;
+import org.briarproject.android.groups.GroupListActivity;
+import org.briarproject.api.ContactId;
+import org.briarproject.api.android.AndroidNotificationManager;
+import org.briarproject.api.messaging.GroupId;
+
+import android.app.Application;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+
+class AndroidNotificationManagerImpl implements AndroidNotificationManager {
+
+ private static final int PRIVATE_MESSAGE_NOTIFICATION_ID = 3;
+ private static final int GROUP_POST_NOTIFICATION_ID = 4;
+
+ private final Context appContext;
+ private final Map contactCounts =
+ new HashMap(); // Locking: this
+ private final Map groupCounts =
+ new HashMap(); // Locking: this
+
+ private int privateTotal = 0, groupTotal = 0; // Locking: this
+
+ @Inject
+ public AndroidNotificationManagerImpl(Application app) {
+ this.appContext = app.getApplicationContext();
+ }
+
+ public synchronized void showPrivateMessageNotification(ContactId c) {
+ Integer count = contactCounts.get(c);
+ if(count == null) contactCounts.put(c, 1);
+ else contactCounts.put(c, count + 1);
+ privateTotal++;
+ updatePrivateMessageNotification();
+ }
+
+ public synchronized void clearPrivateMessageNotification(ContactId c) {
+ Integer count = contactCounts.remove(c);
+ if(count == null) return; // Already cleared
+ privateTotal -= count;
+ updatePrivateMessageNotification();
+ }
+
+ // Locking: this
+ private void updatePrivateMessageNotification() {
+ if(privateTotal == 0) {
+ clearPrivateMessageNotification();
+ } else {
+ NotificationCompat.Builder b =
+ new NotificationCompat.Builder(appContext);
+ b.setSmallIcon(R.drawable.message_notification_icon);
+ b.setContentTitle(appContext.getText(R.string.app_name));
+ b.setContentText(appContext.getText(
+ R.string.private_message_notification_text));
+ b.setDefaults(DEFAULT_ALL);
+ b.setOnlyAlertOnce(true);
+ if(contactCounts.size() == 1) {
+ Intent i = new Intent(appContext, ConversationActivity.class);
+ ContactId c = contactCounts.keySet().iterator().next();
+ i.putExtra("briar.CONTACT_ID", c.getInt());
+ i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
+ TaskStackBuilder tsb = TaskStackBuilder.create(appContext);
+ tsb.addParentStack(ConversationActivity.class);
+ tsb.addNextIntent(i);
+ b.setContentIntent(tsb.getPendingIntent(0, 0));
+ } else {
+ Intent i = new Intent(appContext, ContactListActivity.class);
+ i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
+ TaskStackBuilder tsb = TaskStackBuilder.create(appContext);
+ tsb.addParentStack(ContactListActivity.class);
+ tsb.addNextIntent(i);
+ b.setContentIntent(tsb.getPendingIntent(0, 0));
+ }
+ Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+ NotificationManager nm = (NotificationManager) o;
+ nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
+ }
+ }
+
+ // Locking: this
+ private void clearPrivateMessageNotification() {
+ Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+ NotificationManager nm = (NotificationManager) o;
+ nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
+ }
+
+ public synchronized void showGroupPostNotification(GroupId g) {
+ Integer count = groupCounts.get(g);
+ if(count == null) groupCounts.put(g, 1);
+ else groupCounts.put(g, count + 1);
+ groupTotal++;
+ updateGroupPostNotification();
+ }
+
+ public synchronized void clearGroupPostNotification(GroupId g) {
+ Integer count = groupCounts.remove(g);
+ if(count == null) return; // Already cleared
+ groupTotal -= count;
+ updateGroupPostNotification();
+ }
+
+ // Locking: this
+ private void updateGroupPostNotification() {
+ if(groupTotal == 0) {
+ clearGroupPostNotification();
+ } else {
+ NotificationCompat.Builder b =
+ new NotificationCompat.Builder(appContext);
+ b.setSmallIcon(R.drawable.message_notification_icon);
+ b.setContentTitle(appContext.getText(R.string.app_name));
+ b.setContentText(appContext.getText(
+ R.string.group_post_notification_text));
+ b.setDefaults(DEFAULT_ALL);
+ b.setOnlyAlertOnce(true);
+ if(groupCounts.size() == 1) {
+ Intent i = new Intent(appContext, GroupActivity.class);
+ GroupId g = groupCounts.keySet().iterator().next();
+ i.putExtra("briar.GROUP_ID", g.getBytes());
+ i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
+ TaskStackBuilder tsb = TaskStackBuilder.create(appContext);
+ tsb.addParentStack(GroupActivity.class);
+ tsb.addNextIntent(i);
+ b.setContentIntent(tsb.getPendingIntent(0, 0));
+ } else {
+ Intent i = new Intent(appContext, GroupListActivity.class);
+ i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
+ TaskStackBuilder tsb = TaskStackBuilder.create(appContext);
+ tsb.addParentStack(GroupListActivity.class);
+ tsb.addNextIntent(i);
+ b.setContentIntent(tsb.getPendingIntent(0, 0));
+ }
+ Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+ NotificationManager nm = (NotificationManager) o;
+ nm.notify(GROUP_POST_NOTIFICATION_ID, b.build());
+ }
+ }
+
+ // Locking: this
+ private void clearGroupPostNotification() {
+ Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
+ NotificationManager nm = (NotificationManager) o;
+ nm.cancel(GROUP_POST_NOTIFICATION_ID);
+ }
+
+ public synchronized void clearNotifications() {
+ contactCounts.clear();
+ groupCounts.clear();
+ privateTotal = groupTotal = 0;
+ clearPrivateMessageNotification();
+ clearGroupPostNotification();
+ }
+}
diff --git a/briar-android/src/org/briarproject/android/BriarService.java b/briar-android/src/org/briarproject/android/BriarService.java
index 4e556b2db..bbf576174 100644
--- a/briar-android/src/org/briarproject/android/BriarService.java
+++ b/briar-android/src/org/briarproject/android/BriarService.java
@@ -1,6 +1,5 @@
package org.briarproject.android;
-import static android.app.Notification.DEFAULT_ALL;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
@@ -15,10 +14,9 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import org.briarproject.R;
-import org.briarproject.android.contact.ContactListActivity;
-import org.briarproject.android.groups.GroupListActivity;
import org.briarproject.api.ContactId;
import org.briarproject.api.android.AndroidExecutor;
+import org.briarproject.api.android.AndroidNotificationManager;
import org.briarproject.api.android.DatabaseUiExecutor;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DatabaseConfig;
@@ -38,14 +36,11 @@ import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.TaskStackBuilder;
public class BriarService extends RoboService implements EventListener {
private static final int ONGOING_NOTIFICATION_ID = 1;
private static final int FAILURE_NOTIFICATION_ID = 2;
- private static final int PRIVATE_MESSAGE_NOTIFICATION_ID = 3;
- private static final int GROUP_POST_NOTIFICATION_ID = 4;
private static final Logger LOG =
Logger.getLogger(BriarService.class.getName());
@@ -54,6 +49,7 @@ public class BriarService extends RoboService implements EventListener {
private final Binder binder = new BriarBinder();
@Inject private DatabaseConfig databaseConfig;
+ @Inject private AndroidNotificationManager notificationManager;
// Fields that are accessed from background threads must be volatile
@Inject private volatile LifecycleManager lifecycleManager;
@@ -135,11 +131,8 @@ public class BriarService extends RoboService implements EventListener {
public void onDestroy() {
super.onDestroy();
if(LOG.isLoggable(INFO)) LOG.info("Destroyed");
- Object o = getSystemService(NOTIFICATION_SERVICE);
- NotificationManager nm = (NotificationManager) o;
- nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
- nm.cancel(GROUP_POST_NOTIFICATION_ID);
stopForeground(true);
+ notificationManager.clearNotifications();
// Stop the services in a background thread
new Thread() {
@Override
@@ -168,8 +161,8 @@ public class BriarService extends RoboService implements EventListener {
try {
lifecycleManager.waitForDatabase();
if(g.equals(db.getInboxGroupId(c)))
- showPrivateMessageNotification();
- else showGroupPostNotification();
+ notificationManager.showPrivateMessageNotification(c);
+ else notificationManager.showGroupPostNotification(g);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -182,42 +175,6 @@ public class BriarService extends RoboService implements EventListener {
});
}
- private void showPrivateMessageNotification() {
- NotificationCompat.Builder b = new NotificationCompat.Builder(this);
- b.setSmallIcon(R.drawable.message_notification_icon);
- b.setContentTitle(getText(R.string.private_message_notification_title));
- b.setContentText(getText(R.string.private_message_notification_text));
- b.setAutoCancel(true);
- b.setDefaults(DEFAULT_ALL);
- Intent i = new Intent(this, ContactListActivity.class);
- i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
- TaskStackBuilder tsb = TaskStackBuilder.create(this);
- tsb.addParentStack(ContactListActivity.class);
- tsb.addNextIntent(i);
- b.setContentIntent(tsb.getPendingIntent(0, 0));
- Object o = getSystemService(NOTIFICATION_SERVICE);
- NotificationManager nm = (NotificationManager) o;
- nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
- }
-
- private void showGroupPostNotification() {
- NotificationCompat.Builder b = new NotificationCompat.Builder(this);
- b.setSmallIcon(R.drawable.message_notification_icon);
- b.setContentTitle(getText(R.string.group_post_notification_title));
- b.setContentText(getText(R.string.group_post_notification_text));
- b.setAutoCancel(true);
- b.setDefaults(DEFAULT_ALL);
- Intent i = new Intent(this, GroupListActivity.class);
- i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
- TaskStackBuilder tsb = TaskStackBuilder.create(this);
- tsb.addParentStack(GroupListActivity.class);
- tsb.addNextIntent(i);
- b.setContentIntent(tsb.getPendingIntent(0, 0));
- Object o = getSystemService(NOTIFICATION_SERVICE);
- NotificationManager nm = (NotificationManager) o;
- nm.notify(GROUP_POST_NOTIFICATION_ID, b.build());
- }
-
/** Waits for the database to be opened before returning. */
public void waitForDatabase() throws InterruptedException {
lifecycleManager.waitForDatabase();
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index a3540fe14..e815591c0 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -37,7 +37,9 @@ import org.briarproject.android.util.HorizontalBorder;
import org.briarproject.android.util.LayoutUtils;
import org.briarproject.android.util.ListLoadingProgressBar;
import org.briarproject.api.AuthorId;
+import org.briarproject.api.Contact;
import org.briarproject.api.ContactId;
+import org.briarproject.api.android.AndroidNotificationManager;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
@@ -81,9 +83,9 @@ implements EventListener, OnClickListener, OnItemClickListener {
private static final Logger LOG =
Logger.getLogger(ConversationActivity.class.getName());
+ @Inject private AndroidNotificationManager notificationManager;
@Inject @CryptoExecutor private Executor cryptoExecutor;
private Map bodyCache = new HashMap();
- private String contactName = null;
private TextView empty = null;
private ConversationAdapter adapter = null;
private ListView list = null;
@@ -95,6 +97,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
@Inject private volatile DatabaseComponent db;
@Inject private volatile MessageFactory messageFactory;
private volatile ContactId contactId = null;
+ private volatile String contactName = null;
private volatile GroupId groupId = null;
private volatile Group group = null;
private volatile AuthorId localAuthorId = null;
@@ -107,15 +110,6 @@ implements EventListener, OnClickListener, OnItemClickListener {
int id = i.getIntExtra("briar.CONTACT_ID", -1);
if(id == -1) throw new IllegalStateException();
contactId = new ContactId(id);
- contactName = i.getStringExtra("briar.CONTACT_NAME");
- if(contactName == null) throw new IllegalStateException();
- setTitle(contactName);
- byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
- if(b == null) throw new IllegalStateException();
- groupId = new GroupId(b);
- b = i.getByteArrayExtra("briar.LOCAL_AUTHOR_ID");
- if(b == null) throw new IllegalStateException();
- localAuthorId = new AuthorId(b);
Intent data = new Intent();
data.putExtra("briar.CONTACT_ID", id);
@@ -194,25 +188,59 @@ implements EventListener, OnClickListener, OnItemClickListener {
public void onResume() {
super.onResume();
db.addListener(this);
- loadHeadersAndGroup();
+ loadContactAndGroup();
+ loadHeaders();
}
- private void loadHeadersAndGroup() {
+ private void loadContactAndGroup() {
+ runOnDbThread(new Runnable() {
+ public void run() {
+ try {
+ long now = System.currentTimeMillis();
+ Contact contact = db.getContact(contactId);
+ contactName = contact.getAuthor().getName();
+ localAuthorId = contact.getLocalAuthorId();
+ groupId = db.getInboxGroupId(contactId);
+ group = db.getGroup(groupId);
+ long duration = System.currentTimeMillis() - now;
+ if(LOG.isLoggable(INFO)) {
+ LOG.info("Loading contact and group took "
+ + duration + " ms");
+ }
+ displayContactName();
+ } catch(NoSuchContactException e) {
+ finishOnUiThread();
+ } catch(NoSuchSubscriptionException e) {
+ finishOnUiThread();
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ }
+ }
+ });
+ }
+
+ private void displayContactName() {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ setTitle(contactName);
+ }
+ });
+ }
+
+ private void loadHeaders() {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
Collection headers =
db.getInboxMessageHeaders(contactId);
- group = db.getGroup(groupId);
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
- LOG.info("Load took " + duration + " ms");
+ LOG.info("Loading headers took " + duration + " ms");
displayHeaders(headers);
} catch(NoSuchContactException e) {
finishOnUiThread();
- } catch(NoSuchSubscriptionException e) {
- finishOnUiThread();
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -225,6 +253,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
runOnUiThread(new Runnable() {
public void run() {
loading.setVisibility(GONE);
+ setTitle(contactName);
sendButton.setEnabled(true);
adapter.clear();
if(headers.isEmpty()) {
@@ -306,6 +335,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
}
private void markMessagesRead() {
+ notificationManager.clearPrivateMessageNotification(contactId);
List unread = new ArrayList();
int count = adapter.getCount();
for(int i = 0; i < count; i++) {
@@ -346,11 +376,11 @@ implements EventListener, OnClickListener, OnItemClickListener {
GroupId g = ((MessageAddedEvent) e).getGroup().getId();
if(g.equals(groupId)) {
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
- loadHeadersAndGroup();
+ loadHeaders();
}
} else if(e instanceof MessageExpiredEvent) {
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
- loadHeadersAndGroup();
+ loadHeaders();
}
}
diff --git a/briar-android/src/org/briarproject/android/groups/GroupActivity.java b/briar-android/src/org/briarproject/android/groups/GroupActivity.java
index c38c7985e..ed343db93 100644
--- a/briar-android/src/org/briarproject/android/groups/GroupActivity.java
+++ b/briar-android/src/org/briarproject/android/groups/GroupActivity.java
@@ -27,6 +27,7 @@ import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.HorizontalBorder;
import org.briarproject.android.util.ListLoadingProgressBar;
import org.briarproject.api.Author;
+import org.briarproject.api.android.AndroidNotificationManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.MessageHeader;
@@ -37,6 +38,7 @@ import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageAddedEvent;
import org.briarproject.api.event.MessageExpiredEvent;
import org.briarproject.api.event.SubscriptionRemovedEvent;
+import org.briarproject.api.messaging.Group;
import org.briarproject.api.messaging.GroupId;
import org.briarproject.api.messaging.MessageId;
@@ -59,8 +61,8 @@ OnClickListener, OnItemClickListener {
private static final Logger LOG =
Logger.getLogger(GroupActivity.class.getName());
+ @Inject private AndroidNotificationManager notificationManager;
private Map bodyCache = new HashMap();
- private String groupName = null;
private TextView empty = null;
private GroupAdapter adapter = null;
private ListView list = null;
@@ -69,6 +71,7 @@ OnClickListener, OnItemClickListener {
// Fields that are accessed from background threads must be volatile
@Inject private volatile DatabaseComponent db;
private volatile GroupId groupId = null;
+ private volatile String groupName = null;
@Override
public void onCreate(Bundle state) {
@@ -78,9 +81,6 @@ OnClickListener, OnItemClickListener {
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
- groupName = i.getStringExtra("briar.GROUP_NAME");
- if(groupName == null) throw new IllegalStateException();
- setTitle(groupName);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(MATCH_MATCH);
@@ -128,9 +128,39 @@ OnClickListener, OnItemClickListener {
public void onResume() {
super.onResume();
db.addListener(this);
+ loadGroup();
loadHeaders();
}
+ private void loadGroup() {
+ runOnDbThread(new Runnable() {
+ public void run() {
+ try {
+ long now = System.currentTimeMillis();
+ Group g = db.getGroup(groupId);
+ groupName = g.getName();
+ long duration = System.currentTimeMillis() - now;
+ if(LOG.isLoggable(INFO))
+ LOG.info("Loading group " + duration + " ms");
+ displayGroupName();
+ } catch(NoSuchSubscriptionException e) {
+ finishOnUiThread();
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ }
+ }
+ });
+ }
+
+ private void displayGroupName() {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ setTitle(groupName);
+ }
+ });
+ }
+
private void loadHeaders() {
runOnDbThread(new Runnable() {
public void run() {
@@ -236,6 +266,7 @@ OnClickListener, OnItemClickListener {
}
private void markMessagesRead() {
+ notificationManager.clearGroupPostNotification(groupId);
List unread = new ArrayList();
int count = adapter.getCount();
for(int i = 0; i < count; i++) {
diff --git a/briar-api/src/org/briarproject/api/android/AndroidNotificationManager.java b/briar-api/src/org/briarproject/api/android/AndroidNotificationManager.java
new file mode 100644
index 000000000..c0be8a754
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/android/AndroidNotificationManager.java
@@ -0,0 +1,21 @@
+package org.briarproject.api.android;
+
+import org.briarproject.api.ContactId;
+import org.briarproject.api.messaging.GroupId;
+
+/**
+ * Manages notifications for private messages and group posts. All methods must
+ * be called from the Android UI thread.
+ */
+public interface AndroidNotificationManager {
+
+ public void showPrivateMessageNotification(ContactId c);
+
+ public void clearPrivateMessageNotification(ContactId c);
+
+ public void showGroupPostNotification(GroupId g);
+
+ public void clearGroupPostNotification(GroupId g);
+
+ public void clearNotifications();
+}