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