From ea0e00f4acfc5f1ef35b75ece8b2c14f8c476b80 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Tue, 5 Dec 2017 12:09:22 +0000
Subject: [PATCH] Use channels for all notifications.

---
 .../AndroidNotificationManagerImpl.java       | 100 +++++++++++-------
 .../briar/android/BriarService.java           |  49 ++++++---
 .../briar/android/StartupFailureActivity.java |   7 +-
 .../briar/android/activity/BriarActivity.java |   5 +-
 .../android/navdrawer/NavDrawerActivity.java  |   3 +-
 .../util/BriarNotificationBuilder.java        |   4 +-
 6 files changed, 107 insertions(+), 61 deletions(-)

diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
index 41463fe850..5baa6830a9 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
@@ -1,11 +1,14 @@
 package org.briarproject.briar.android;
 
+import android.annotation.TargetApi;
 import android.app.Application;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.support.annotation.StringRes;
 import android.support.annotation.UiThread;
 import android.support.v4.app.TaskStackBuilder;
 
@@ -44,6 +47,7 @@ import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
@@ -57,8 +61,10 @@ import javax.inject.Inject;
 import static android.app.Notification.DEFAULT_LIGHTS;
 import static android.app.Notification.DEFAULT_SOUND;
 import static android.app.Notification.DEFAULT_VIBRATE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.content.Context.NOTIFICATION_SERVICE;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.os.Build.VERSION.SDK_INT;
 import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
 import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
 import static java.util.logging.Level.WARNING;
@@ -83,6 +89,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	private static final int BLOG_POST_NOTIFICATION_ID = 6;
 	private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7;
 
+	// Channel IDs
+	private static final String CONTACT_CHANNEL_ID = "contacts";
+	private static final String GROUP_CHANNEL_ID = "groups";
+	private static final String FORUM_CHANNEL_ID = "forums";
+	private static final String BLOG_CHANNEL_ID = "blogs";
+
 	private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
 
 	private static final Logger LOG =
@@ -91,8 +103,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	private final Executor dbExecutor;
 	private final SettingsManager settingsManager;
 	private final AndroidExecutor androidExecutor;
-	private final Context appContext;
 	private final Clock clock;
+	private final Context appContext;
+	private final NotificationManager notificationManager;
 	private final AtomicBoolean used = new AtomicBoolean(false);
 
 	// The following must only be accessed on the main UI thread
@@ -121,6 +134,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		this.androidExecutor = androidExecutor;
 		this.clock = clock;
 		appContext = app.getApplicationContext();
+		notificationManager = (NotificationManager)
+				appContext.getSystemService(NOTIFICATION_SERVICE);
 	}
 
 	@Override
@@ -132,6 +147,33 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		} catch (DbException e) {
 			throw new ServiceException(e);
 		}
+		if (SDK_INT >= 26) {
+			// Create notification channels
+			Callable<Void> task = () -> {
+				createNotificationChannel(CONTACT_CHANNEL_ID,
+						R.string.contact_list_button);
+				createNotificationChannel(GROUP_CHANNEL_ID,
+						R.string.groups_button);
+				createNotificationChannel(FORUM_CHANNEL_ID,
+						R.string.forums_button);
+				createNotificationChannel(BLOG_CHANNEL_ID,
+						R.string.blogs_button);
+				return null;
+			};
+			try {
+				androidExecutor.runOnUiThread(task).get();
+			} catch (InterruptedException | ExecutionException e) {
+				throw new ServiceException(e);
+			}
+		}
+	}
+
+	@TargetApi(26)
+	private void createNotificationChannel(String channelId,
+			@StringRes int name) {
+		notificationManager.createNotificationChannel(
+				new NotificationChannel(channelId, appContext.getString(name),
+						IMPORTANCE_DEFAULT));
 	}
 
 	@Override
@@ -156,44 +198,34 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	private void clearContactNotification() {
 		contactCounts.clear();
 		contactTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
+		notificationManager.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
 	}
 
 	@UiThread
 	private void clearGroupMessageNotification() {
 		groupCounts.clear();
 		groupTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
+		notificationManager.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
 	}
 
 	@UiThread
 	private void clearForumPostNotification() {
 		forumCounts.clear();
 		forumTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(FORUM_POST_NOTIFICATION_ID);
+		notificationManager.cancel(FORUM_POST_NOTIFICATION_ID);
 	}
 
 	@UiThread
 	private void clearBlogPostNotification() {
 		blogCounts.clear();
 		blogTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(BLOG_POST_NOTIFICATION_ID);
+		notificationManager.cancel(BLOG_POST_NOTIFICATION_ID);
 	}
 
 	@UiThread
 	private void clearIntroductionSuccessNotification() {
 		introductionTotal = 0;
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
+		notificationManager.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
 	}
 
 	@Override
@@ -269,8 +301,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		if (contactTotal == 0) {
 			clearContactNotification();
 		} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
-			BriarNotificationBuilder b =
-					new BriarNotificationBuilder(appContext);
+			BriarNotificationBuilder b = new BriarNotificationBuilder(
+					appContext, CONTACT_CHANNEL_ID);
 			b.setSmallIcon(R.drawable.notification_private_message);
 			b.setColorRes(R.color.briar_primary);
 			b.setContentTitle(appContext.getText(R.string.app_name));
@@ -305,9 +337,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				t.addNextIntent(i);
 				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 			}
-			Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-			NotificationManager nm = (NotificationManager) o;
-			nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
+			notificationManager.notify(PRIVATE_MESSAGE_NOTIFICATION_ID,
+					b.build());
 		}
 	}
 
@@ -378,7 +409,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			clearGroupMessageNotification();
 		} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
 			BriarNotificationBuilder b =
-					new BriarNotificationBuilder(appContext);
+					new BriarNotificationBuilder(appContext, GROUP_CHANNEL_ID);
 			b.setSmallIcon(R.drawable.notification_private_group);
 			b.setColorRes(R.color.briar_primary);
 			b.setContentTitle(appContext.getText(R.string.app_name));
@@ -414,9 +445,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				t.addNextIntent(i);
 				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 			}
-			Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-			NotificationManager nm = (NotificationManager) o;
-			nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
+			notificationManager.notify(GROUP_MESSAGE_NOTIFICATION_ID,
+					b.build());
 		}
 	}
 
@@ -455,7 +485,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			clearForumPostNotification();
 		} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
 			BriarNotificationBuilder b =
-					new BriarNotificationBuilder(appContext);
+					new BriarNotificationBuilder(appContext, FORUM_CHANNEL_ID);
 			b.setSmallIcon(R.drawable.notification_forum);
 			b.setColorRes(R.color.briar_primary);
 			b.setContentTitle(appContext.getText(R.string.app_name));
@@ -491,9 +521,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 				t.addNextIntent(i);
 				b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 			}
-			Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-			NotificationManager nm = (NotificationManager) o;
-			nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
+			notificationManager.notify(FORUM_POST_NOTIFICATION_ID, b.build());
 		}
 	}
 
@@ -532,7 +560,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			clearBlogPostNotification();
 		} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
 			BriarNotificationBuilder b =
-					new BriarNotificationBuilder(appContext);
+					new BriarNotificationBuilder(appContext, BLOG_CHANNEL_ID);
 			b.setSmallIcon(R.drawable.notification_blog);
 			b.setColorRes(R.color.briar_primary);
 			b.setContentTitle(appContext.getText(R.string.app_name));
@@ -555,9 +583,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			t.addNextIntent(i);
 			b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 
-			Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-			NotificationManager nm = (NotificationManager) o;
-			nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
+			notificationManager.notify(BLOG_POST_NOTIFICATION_ID, b.build());
 		}
 	}
 
@@ -577,7 +603,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 
 	@UiThread
 	private void updateIntroductionNotification() {
-		BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
+		BriarNotificationBuilder b =
+				new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
 		b.setSmallIcon(R.drawable.notification_introduction);
 		b.setColorRes(R.color.briar_primary);
 		b.setContentTitle(appContext.getText(R.string.app_name));
@@ -599,9 +626,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		t.addNextIntent(i);
 		b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
 
-		Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
+		notificationManager.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID,
+				b.build());
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
index bdbd9f8a07..2ff15e494d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
@@ -26,6 +26,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -38,9 +40,19 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
 
 public class BriarService extends Service {
 
+	public static String EXTRA_START_RESULT =
+			"org.briarproject.briar.START_RESULT";
+	public static String EXTRA_NOTIFICATION_ID =
+			"org.briarproject.briar.FAILURE_NOTIFICATION_ID";
+	public static String EXTRA_STARTUP_FAILED =
+			"org.briarproject.briar.STARTUP_FAILED";
+
 	private static final int ONGOING_NOTIFICATION_ID = 1;
 	private static final int FAILURE_NOTIFICATION_ID = 2;
 
+	private static final String ONGOING_CHANNEL_ID = "foregroundService";
+	private static final String FAILURE_CHANNEL_ID = "startupFailure";
+
 	private static final Logger LOG =
 			Logger.getLogger(BriarService.class.getName());
 
@@ -74,20 +86,26 @@ public class BriarService extends Service {
 			stopSelf();
 			return;
 		}
-		// Create mandatory notification channel
-		String channelId = "foregroundService";
+		// Create notification channels
 		if (Build.VERSION.SDK_INT >= 26) {
-			NotificationChannel channel = new NotificationChannel(channelId,
-					getString(R.string.app_name),
-					NotificationManager.IMPORTANCE_NONE);
-			channel.setLockscreenVisibility(VISIBILITY_SECRET);
-			NotificationManager nm =
-					(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-			nm.createNotificationChannel(channel);
+			NotificationManager nm = (NotificationManager)
+					getSystemService(NOTIFICATION_SERVICE);
+			NotificationChannel ongoingChannel = new NotificationChannel(
+					ONGOING_CHANNEL_ID,
+					getString(R.string.ongoing_notification_title),
+					IMPORTANCE_NONE);
+			ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
+			nm.createNotificationChannel(ongoingChannel);
+			NotificationChannel failureChannel = new NotificationChannel(
+					FAILURE_CHANNEL_ID,
+					getString(R.string.startup_failed_notification_title),
+					IMPORTANCE_DEFAULT);
+			failureChannel.setLockscreenVisibility(VISIBILITY_SECRET);
+			nm.createNotificationChannel(failureChannel);
 		}
 		// Show an ongoing notification that the service is running
 		NotificationCompat.Builder b =
-				new NotificationCompat.Builder(this, channelId);
+				new NotificationCompat.Builder(this, ONGOING_CHANNEL_ID);
 		b.setSmallIcon(R.drawable.notification_ongoing);
 		b.setColor(ContextCompat.getColor(this, R.color.briar_primary));
 		b.setContentTitle(getText(R.string.ongoing_notification_title));
@@ -123,8 +141,8 @@ public class BriarService extends Service {
 
 	private void showStartupFailureNotification(StartResult result) {
 		androidExecutor.runOnUiThread(() -> {
-			NotificationCompat.Builder b =
-					new NotificationCompat.Builder(BriarService.this);
+			NotificationCompat.Builder b = new NotificationCompat.Builder(
+					BriarService.this, FAILURE_CHANNEL_ID);
 			b.setSmallIcon(android.R.drawable.stat_notify_error);
 			b.setContentTitle(getText(
 					R.string.startup_failed_notification_title));
@@ -133,9 +151,8 @@ public class BriarService extends Service {
 			Intent i = new Intent(BriarService.this,
 					StartupFailureActivity.class);
 			i.setFlags(FLAG_ACTIVITY_NEW_TASK);
-			i.putExtra("briar.START_RESULT", result);
-			i.putExtra("briar.FAILURE_NOTIFICATION_ID",
-					FAILURE_NOTIFICATION_ID);
+			i.putExtra(EXTRA_START_RESULT, result);
+			i.putExtra(EXTRA_NOTIFICATION_ID, FAILURE_NOTIFICATION_ID);
 			b.setContentIntent(PendingIntent.getActivity(BriarService.this,
 					0, i, FLAG_UPDATE_CURRENT));
 			Object o = getSystemService(NOTIFICATION_SERVICE);
@@ -144,7 +161,7 @@ public class BriarService extends Service {
 			// Bring the dashboard to the front to clear the back stack
 			i = new Intent(BriarService.this, NavDrawerActivity.class);
 			i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
-			i.putExtra("briar.STARTUP_FAILED", true);
+			i.putExtra(EXTRA_STARTUP_FAILED, true);
 			startActivity(i);
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/StartupFailureActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/StartupFailureActivity.java
index dae9706d68..2b8288e06a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/StartupFailureActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/StartupFailureActivity.java
@@ -10,6 +10,8 @@ import org.briarproject.briar.android.activity.ActivityComponent;
 import org.briarproject.briar.android.activity.BaseActivity;
 
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
+import static org.briarproject.briar.android.BriarService.EXTRA_NOTIFICATION_ID;
+import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
 
 public class StartupFailureActivity extends BaseActivity {
 
@@ -27,8 +29,9 @@ public class StartupFailureActivity extends BaseActivity {
 	}
 
 	private void handleIntent(Intent i) {
-		StartResult result = (StartResult) i.getSerializableExtra("briar.START_RESULT");
-		int notificationId = i.getIntExtra("briar.FAILURE_NOTIFICATION_ID", -1);
+		StartResult result =
+				(StartResult) i.getSerializableExtra(EXTRA_START_RESULT);
+		int notificationId = i.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
 
 		// cancel notification
 		if (notificationId > -1) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
index b52d492bd8..823f7ad64a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
@@ -36,7 +36,6 @@ import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingInt
 @SuppressLint("Registered")
 public abstract class BriarActivity extends BaseActivity {
 
-	public static final String KEY_STARTUP_FAILED = "briar.STARTUP_FAILED";
 	public static final String GROUP_ID = "briar.GROUP_ID";
 	public static final String GROUP_NAME = "briar.GROUP_NAME";
 
@@ -131,8 +130,8 @@ public abstract class BriarActivity extends BaseActivity {
 		b.setNegativeButton(R.string.cancel,
 				(dialog, which) -> dialog.dismiss());
 		b.setOnDismissListener(dialog -> {
-			CheckBox checkBox = (CheckBox) ((AlertDialog) dialog)
-					.findViewById(R.id.checkbox);
+			CheckBox checkBox =
+					((AlertDialog) dialog).findViewById(R.id.checkbox);
 			if (checkBox.isChecked())
 				briarController.doNotAskAgainForDozeWhiteListing();
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
index 468d88cd0e..99c2c586fb 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
@@ -51,6 +51,7 @@ import static android.support.v4.view.GravityCompat.START;
 import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
+import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
 import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
 import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
@@ -167,7 +168,7 @@ public class NavDrawerActivity extends BriarActivity implements
 	}
 
 	private void exitIfStartupFailed(Intent intent) {
-		if (intent.getBooleanExtra(KEY_STARTUP_FAILED, false)) {
+		if (intent.getBooleanExtra(EXTRA_STARTUP_FAILED, false)) {
 			finish();
 			LOG.info("Exiting");
 			System.exit(0);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/BriarNotificationBuilder.java b/briar-android/src/main/java/org/briarproject/briar/android/util/BriarNotificationBuilder.java
index 2db7fdd1fb..6e80004e4c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/BriarNotificationBuilder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/BriarNotificationBuilder.java
@@ -12,8 +12,8 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
 
 public class BriarNotificationBuilder extends NotificationCompat.Builder {
 
-	public BriarNotificationBuilder(Context context) {
-		super(context);
+	public BriarNotificationBuilder(Context context, String channelId) {
+		super(context, channelId);
 		// Auto-cancel does not fire the delete intent, see
 		// https://issuetracker.google.com/issues/36961721
 		setAutoCancel(true);
-- 
GitLab