Bug 1476720 - Set MLS notification action. r?sdaswani draft
authorVlad Baicu <vlad.baicu@softvision.ro>
Thu, 26 Jul 2018 17:42:19 +0300
changeset 823098 52c21f4c0eb9c13e630d554eceb8c77a78f55860
parent 823097 35bda3090f064a5ff42720c04b2a136d92ad5e88
push id117561
push uservbaicu@mozilla.com
push dateThu, 26 Jul 2018 14:43:54 +0000
reviewerssdaswani
bugs1476720
milestone63.0a1
Bug 1476720 - Set MLS notification action. r?sdaswani Moved notification channels to NotificationHelper and created new utility methods to retrieve them when needed. Cleaned unused imports. MozReview-Commit-ID: L3HnV3nD7Og
mobile/android/base/java/org/mozilla/gecko/DataReportingNotification.java
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/GuestSession.java
mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaControlAgent.java
mobile/android/base/java/org/mozilla/gecko/mma/MmaLeanplumImp.java
mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
mobile/android/base/java/org/mozilla/gecko/notifications/WhatsNewReceiver.java
mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueHelper.java
mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueService.java
mobile/android/base/java/org/mozilla/gecko/tabqueue/TabReceivedService.java
mobile/android/base/java/org/mozilla/gecko/updater/Updater.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/strings.xml.in
mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountNotificationManager.java
mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/StumblerService.java
--- a/mobile/android/base/java/org/mozilla/gecko/DataReportingNotification.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DataReportingNotification.java
@@ -14,16 +14,17 @@ import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.support.v4.app.NotificationCompat;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.style.StyleSpan;
 
 import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 
 public class DataReportingNotification {
 
     private static final String LOGTAG = "DataReportNotification";
 
     public static final String ALERT_NAME_DATAREPORTING_NOTIFICATION = "datareporting-notification";
 
@@ -104,17 +105,18 @@ public class DataReportingNotification {
                                         .setAutoCancel(true)
                                         .setContentIntent(contentIntent)
                                         .setStyle(new NotificationCompat.BigTextStyle()
                                                                         .bigText(notificationBigSummary))
                                         .addAction(R.drawable.firefox_settings_alert, notificationAction, contentIntent)
                                         .setTicker(tickerText);
 
             if (!AppConstants.Versions.preO) {
-                notificationBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+                notificationBuilder.setChannelId(NotificationHelper.getInstance(context)
+                        .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
             }
 
             NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
             int notificationID = ALERT_NAME_DATAREPORTING_NOTIFICATION.hashCode();
             notificationManager.notify(notificationID, notificationBuilder.build());
 
             // Record version and notification time.
             SharedPreferences.Editor editor = sharedPrefs.edit();
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -1,20 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.Manifest;
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Application;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
@@ -72,22 +69,16 @@ import java.net.URL;
 import java.util.UUID;
 
 public class GeckoApplication extends Application
                               implements HapticFeedbackDelegate {
     private static final String LOG_TAG = "GeckoApplication";
     public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
     private static final String MEDIA_DECODING_PROCESS_CRASH = "MEDIA_DECODING_PROCESS_CRASH";
 
-    private static NotificationChannel defaultNotificationChannel = null;
-    /**
-     * Mozilla Location Services Notification Channel.
-     */
-    private static NotificationChannel mlsNotificationChannel = null;
-
     private boolean mInBackground;
     private boolean mPausedGecko;
     private boolean mIsInitialResume;
 
     private LightweightTheme mLightweightTheme;
 
     private RefWatcher mRefWatcher;
 
@@ -355,21 +346,16 @@ public class GeckoApplication extends Ap
         NotificationHelper.getInstance(context).init();
 
         MulticastDNSManager.getInstance(context).init();
 
         GeckoService.register();
 
         IntentHelper.init();
 
-        if (!AppConstants.Versions.preO) {
-            createDefaultNotificationChannel();
-            createMLSNotificationChannel();
-        }
-
         EventDispatcher.getInstance().registerGeckoThreadListener(mListener,
                 "Distribution:GetDirectories",
                 null);
         EventDispatcher.getInstance().registerUiThreadListener(mListener,
                 "Gecko:Exited",
                 "RuntimePermissions:Check",
                 "Snackbar:Show",
                 "Share:Text",
@@ -433,44 +419,16 @@ public class GeckoApplication extends Ap
             final Method onCreate = clazz.getMethod("onCreate", Context.class);
             return (Boolean) onCreate.invoke(null, getApplicationContext()); // Method is static.
         } catch (Exception e) {
             Log.e(LOG_TAG, "Got exception during startup; ignoring.", e);
             return false;
         }
     }
 
-    @TargetApi(26)
-    private void createDefaultNotificationChannel() {
-        final String DEFAULT_CHANNEL = AppConstants.MOZ_APP_DISPLAYNAME;
-        final String DEFAULT_NAME = AppConstants.MOZ_APP_DISPLAYNAME;
-        final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_HIGH;
-
-        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-        defaultNotificationChannel = notificationManager.getNotificationChannel(DEFAULT_CHANNEL);
-        if (defaultNotificationChannel == null) {
-            defaultNotificationChannel = new NotificationChannel(DEFAULT_CHANNEL, DEFAULT_NAME, DEFAULT_IMPORTANCE);
-            notificationManager.createNotificationChannel(defaultNotificationChannel);
-        }
-    }
-
-    @TargetApi(26)
-    private void createMLSNotificationChannel() {
-        final String DEFAULT_CHANNEL = AppConstants.MOZ_APP_DISPLAYNAME;
-        final String DEFAULT_NAME = AppConstants.MOZ_APP_DISPLAYNAME;
-        final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_LOW;
-
-        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-        mlsNotificationChannel = notificationManager.getNotificationChannel(DEFAULT_CHANNEL);
-        if (mlsNotificationChannel == null) {
-            mlsNotificationChannel = new NotificationChannel(DEFAULT_CHANNEL, DEFAULT_NAME, DEFAULT_IMPORTANCE);
-            notificationManager.createNotificationChannel(mlsNotificationChannel);
-        }
-    }
-
     public void onDelayedStartup() {
         if (AppConstants.MOZ_ANDROID_GCM) {
             // TODO: only run in main process.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     initPushService();
                 }
@@ -656,24 +614,16 @@ public class GeckoApplication extends Ap
                 // Init push service and redirect the event to it.
                 if (initPushService()) {
                     EventDispatcher.getInstance().dispatch(event, message, callback);
                 }
             }
         }
     }
 
-    public static NotificationChannel getDefaultNotificationChannel() {
-        return defaultNotificationChannel;
-    }
-
-    public static NotificationChannel getMLSNotificationChannel() {
-        return mlsNotificationChannel;
-    }
-
     public boolean isApplicationInBackground() {
         return mInBackground;
     }
 
     public LightweightTheme getLightweightTheme() {
         return mLightweightTheme;
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/GuestSession.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GuestSession.java
@@ -6,16 +6,18 @@ package org.mozilla.gecko;
 
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.support.v7.app.NotificationCompat;
 
+import org.mozilla.gecko.notifications.NotificationHelper;
+
 // Utility methods for entering/exiting guest mode.
 public final class GuestSession {
     private static final String LOGTAG = "GeckoGuestSession";
 
     public static final String NOTIFICATION_INTENT = "org.mozilla.gecko.GUEST_SESSION_INPROGRESS";
 
     private static PendingIntent getNotificationIntent(Context context) {
         Intent intent = new Intent(NOTIFICATION_INTENT);
@@ -29,17 +31,18 @@ public final class GuestSession {
         final Resources res = context.getResources();
         builder.setContentTitle(res.getString(R.string.guest_browsing_notification_title))
                .setContentText(res.getString(R.string.guest_browsing_notification_text))
                .setSmallIcon(R.drawable.alert_guest)
                .setOngoing(true)
                .setContentIntent(getNotificationIntent(context));
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         final NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         manager.notify(R.id.guestNotification, builder.build());
     }
 
     public static void hideNotification(Context context) {
         final NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaControlAgent.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaControlAgent.java
@@ -26,22 +26,22 @@ import android.support.annotation.CheckR
 import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.app.NotificationManagerCompat;
 import android.util.Log;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.IntentHelper;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.annotation.RobocopTarget;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import static org.mozilla.gecko.BuildConfig.DEBUG;
 
 public class GeckoMediaControlAgent {
     private static final String LOGTAG = "GeckoMediaControlAgent";
 
@@ -391,17 +391,18 @@ public class GeckoMediaControlAgent {
                 .setStyle(style)
                 .addAction(createNotificationAction())
                 .setOngoing(onGoing)
                 .setShowWhen(false)
                 .setWhen(0)
                 .setVisibility(visibility);
 
         if (!AppConstants.Versions.preO) {
-            notificationBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            notificationBuilder.setChannelId(NotificationHelper.getInstance(mContext)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         currentNotification = notificationBuilder.build();
     }
 
     /* package */ Notification getCurrentNotification() {
         return currentNotification;
     }
--- a/mobile/android/base/java/org/mozilla/gecko/mma/MmaLeanplumImp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/mma/MmaLeanplumImp.java
@@ -20,19 +20,19 @@ import com.leanplum.LeanplumActivityHelp
 import com.leanplum.LeanplumPushNotificationCustomizer;
 import com.leanplum.LeanplumPushService;
 import com.leanplum.annotations.Parser;
 import com.leanplum.callbacks.VariablesChangedCallback;
 import com.leanplum.internal.Constants;
 import com.leanplum.internal.LeanplumInternal;
 
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.MmaConstants;
 import org.mozilla.gecko.firstrun.PanelConfig;
+import org.mozilla.gecko.notifications.NotificationHelper;
 
 import java.lang.ref.WeakReference;
 import java.util.Map;
 
 
 public class MmaLeanplumImp implements MmaInterface {
 
 
@@ -87,17 +87,18 @@ public class MmaLeanplumImp implements M
     @Override
     public void setCustomIcon(@DrawableRes final int iconResId) {
         LeanplumPushService.setCustomizer(new LeanplumPushNotificationCustomizer() {
             @Override
             public void customize(NotificationCompat.Builder builder, Bundle notificationPayload) {
                 builder.setSmallIcon(iconResId);
                 builder.setDefaults(Notification.DEFAULT_SOUND);
                 if (!AppConstants.Versions.preO) {
-                    builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+                    builder.setChannelId(NotificationHelper.getInstance(builder.mContext)
+                            .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
                 }
             }
         });
     }
 
     @Override
     public void start(Context context) {
 
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
@@ -15,17 +15,16 @@ import android.graphics.Bitmap;
 import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationManagerCompat;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoActivityMonitor;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoService;
 import org.mozilla.gecko.NotificationListener;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.util.BitmapUtils;
 
 import java.util.HashMap;
 
 /**
@@ -151,17 +150,18 @@ public final class NotificationClient im
                 .setDeleteIntent(deleteIntent)
                 .setAutoCancel(true)
                 .setDefaults(Notification.DEFAULT_SOUND)
                 .setStyle(new NotificationCompat.BigTextStyle()
                         .bigText(alertText)
                         .setSummaryText(host));
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(mContext)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         // Fetch icon.
         if (!imageUrl.isEmpty()) {
             final Bitmap image = BitmapUtils.decodeUrl(imageUrl);
             builder.setLargeIcon(image);
         }
 
@@ -232,17 +232,18 @@ public final class NotificationClient im
         final Notification.Builder notificationBuilder = new Notification.Builder(mContext)
                 .setContentText(alertText)
                 .setSmallIcon(notification.icon)
                 .setWhen(notification.when)
                 .setContentIntent(notification.contentIntent)
                 .setProgress((int) progressMax, (int) progress, false);
 
         if (!AppConstants.Versions.preO) {
-            notificationBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            notificationBuilder.setChannelId(NotificationHelper.getInstance(mContext)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         notification = notificationBuilder.build();
         add(name, notification);
     }
 
     /* package */ synchronized Notification onNotificationClose(final String name) {
         mNotificationManager.cancel(name, 0);
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
@@ -1,16 +1,19 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.notifications;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -18,29 +21,31 @@ import android.os.StrictMode;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.util.SimpleArrayMap;
 import android.util.Log;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoActivityMonitor;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoApplication;
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.BitmapUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.io.File;
 import java.io.UnsupportedEncodingException;
 import java.net.URLConnection;
 import java.net.URLDecoder;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public final class NotificationHelper implements BundleEventListener {
     public static final String HELPER_BROADCAST_ACTION = AppConstants.ANDROID_PACKAGE_NAME + ".helperBroadcastAction";
 
     public static final String NOTIFICATION_ID = "NotificationHelper_ID";
     private static final String LOGTAG = "GeckoNotificationHelper";
     private static final String HELPER_NOTIFICATION = "helperNotif";
 
@@ -73,16 +78,36 @@ public final class NotificationHelper im
     private static final String BUTTON_EVENT = "notification-button-clicked";
     private static final String CLICK_EVENT = "notification-clicked";
     static final String CLEARED_EVENT = "notification-cleared";
 
     static final String ORIGINAL_EXTRA_COMPONENT = "originalComponent";
 
     private final Context mContext;
 
+
+    public enum Channel {
+        /**
+         * Default notification channel.
+         */
+        DEFAULT,
+        /**
+         * Mozilla Location Services notification channel.
+         */
+        MLS
+    }
+
+    private final Map<Channel, String> mDefinedNotificationChannels = new HashMap<Channel, String>() {{
+        final String DEFAULT_CHANNEL_TAG = "default-notification-channel";
+        put(Channel.DEFAULT, DEFAULT_CHANNEL_TAG);
+
+        final String MLS_CHANNEL_TAG     = "mls-notification-channel";
+        put(Channel.MLS, MLS_CHANNEL_TAG);
+    }};
+
     // Holds a list of notifications that should be cleared if the Fennec Activity is shut down.
     // Will not include ongoing or persistent notifications that are tied to Gecko's lifecycle.
     private SimpleArrayMap<String, GeckoBundle> mClearableNotifications;
 
     private boolean mInitialized;
     private static NotificationHelper sInstance;
 
     private NotificationHelper(Context context) {
@@ -93,31 +118,74 @@ public final class NotificationHelper im
         if (mInitialized) {
             return;
         }
 
         mClearableNotifications = new SimpleArrayMap<>();
         EventDispatcher.getInstance().registerUiThreadListener(this,
             "Notification:Show",
             "Notification:Hide");
+
+        if (!AppConstants.Versions.preO) {
+            initNotificationChannels();
+        }
+
         mInitialized = true;
     }
 
     public static NotificationHelper getInstance(Context context) {
         // If someone else created this singleton, but didn't initialize it, something has gone wrong.
         if (sInstance != null && !sInstance.mInitialized) {
             throw new IllegalStateException("NotificationHelper was created by someone else but not initialized");
         }
 
         if (sInstance == null) {
             sInstance = new NotificationHelper(context.getApplicationContext());
         }
         return sInstance;
     }
 
+    private void initNotificationChannels() {
+        for (Channel mozChannel : mDefinedNotificationChannels.keySet()) {
+            createChannel(mozChannel);
+        }
+    }
+
+    @TargetApi(26)
+    private void createChannel(Channel definedChannel) {
+        final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        NotificationChannel channel = notificationManager.getNotificationChannel(mDefinedNotificationChannels.get(definedChannel));
+
+        if (channel == null) {
+            switch (definedChannel) {
+                case MLS: {
+                    channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
+                            mContext.getString(R.string.mls_notification_channel), NotificationManager.IMPORTANCE_LOW);
+                }
+                break;
+
+                case DEFAULT:
+
+                default: {
+                    channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
+                            mContext.getString(R.string.default_notification_channel), NotificationManager.IMPORTANCE_HIGH);
+                }
+                break;
+            }
+
+            notificationManager.createNotificationChannel(channel);
+        }
+    }
+
+    @TargetApi(26)
+    public NotificationChannel getNotificationChannel(Channel definedChannel) {
+        final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        return notificationManager.getNotificationChannel(mDefinedNotificationChannels.get(definedChannel));
+    }
+
     @Override // BundleEventListener
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
         if ("Notification:Show".equals(event)) {
             showNotification(message);
         } else if ("Notification:Hide".equals(event)) {
             hideNotification(message);
         }
@@ -247,17 +315,17 @@ public final class NotificationHelper im
         builder.setSmallIcon(BitmapUtils.getResource(mContext, imageUri));
 
         final int[] light = message.getIntArray(LIGHT_ATTR);
         if (light != null && light.length == 3) {
             builder.setLights(light[0], light[1], light[2]);
         }
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(getNotificationChannel(Channel.DEFAULT).getId());
         }
 
         final boolean ongoing = message.getBoolean(ONGOING_ATTR);
         builder.setOngoing(ongoing);
 
         if (message.containsKey(WHEN_ATTR)) {
             final long when = (long) message.getDouble(WHEN_ATTR);
             builder.setWhen(when);
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/WhatsNewReceiver.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/WhatsNewReceiver.java
@@ -1,26 +1,24 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.notifications;
 
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.support.v4.app.NotificationCompat;
 import android.text.TextUtils;
 
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.switchboard.SwitchBoard;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.Locales;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.preferences.GeckoPreferences;
@@ -66,17 +64,18 @@ public class WhatsNewReceiver extends Br
                 .setContentTitle(context.getString(R.string.whatsnew_notification_title))
                 .setContentText(context.getString(R.string.whatsnew_notification_summary))
                 .setSmallIcon(R.drawable.ic_status_logo)
                 .setAutoCancel(true)
                 .setContentIntent(getContentIntent(context))
                 .setDeleteIntent(getDeleteIntent(context));
 
         if (!AppConstants.Versions.preO) {
-            notificationBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            notificationBuilder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         final int notificationID = EXTRA_WHATSNEW_NOTIFICATION.hashCode();
         final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.notify(notificationID, notificationBuilder.build());
 
         Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.NOTIFICATION, EXTRA_WHATSNEW_NOTIFICATION);
     }
--- a/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueHelper.java
@@ -18,20 +18,20 @@ import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.WindowManager;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class TabQueueHelper {
@@ -250,17 +250,18 @@ public class TabQueueHelper {
                                                      .setContentTitle(text)
                                                      .setContentText(resources.getString(R.string.tab_queue_notification_title))
                                                      .setStyle(inboxStyle)
                                                      .setColor(ContextCompat.getColor(context, R.color.fennec_ui_accent))
                                                      .setNumber(tabsQueued)
                                                      .setContentIntent(pendingIntent);
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.notify(TabQueueHelper.TAB_QUEUE_NOTIFICATION_ID, builder.build());
     }
 
     public static boolean shouldOpenTabQueueUrls(final Context context) {
         ThreadUtils.assertNotOnUiThread();
--- a/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabQueueService.java
@@ -2,51 +2,49 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tabqueue;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.provider.Settings;
 import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationManagerCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.TextView;
 import android.widget.Toast;
 
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.mozglue.SafeIntent;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 
 import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 
 /**
@@ -259,17 +257,18 @@ public class TabQueueService extends Ser
                 .setStyle(style)
                 .setSmallIcon(R.drawable.ic_status_logo)
                 .setContentIntent(pendingIntent)
                 .setPriority(NotificationCompat.PRIORITY_MAX)
                 .setAutoCancel(true)
                 .addAction(R.drawable.ic_action_settings, getString(R.string.tab_queue_prompt_settings_button), pendingIntent);
 
         if (!AppConstants.Versions.preO) {
-            notificationBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            notificationBuilder.setChannelId(NotificationHelper.getInstance(this)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.notify(R.id.tabQueueSettingsNotification, notificationBuilder.build());
     }
 
     private void removeView() {
         try {
--- a/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabReceivedService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabqueue/TabReceivedService.java
@@ -17,20 +17,20 @@ import android.support.annotation.Nullab
 import android.support.annotation.WorkerThread;
 import android.support.v4.app.JobIntentService;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationManagerCompat;
 import android.util.Log;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserLocaleManager;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.notifications.NotificationHelper;
 
 /**
  * A JobIntentService that displays a notification for a tab sent to this device.
  *
  * The expected Intent should contain:
  *   * Data: URI to open in the notification
  *   * EXTRA_TITLE: Page title of the URI to open
  */
@@ -64,17 +64,18 @@ public class TabReceivedService extends 
         builder.setSmallIcon(R.drawable.ic_status_logo);
         builder.setContentTitle(notificationTitle);
         builder.setWhen(System.currentTimeMillis());
         builder.setAutoCancel(true);
         builder.setContentText(uri);
         builder.setContentIntent(contentIntent);
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(this)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         // Trigger "heads-up" notification mode on supported Android versions.
         builder.setPriority(NotificationCompat.PRIORITY_HIGH);
         final Uri notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
         if (notificationSoundUri != null) {
             builder.setSound(notificationSoundUri);
         }
--- a/mobile/android/base/java/org/mozilla/gecko/updater/Updater.java
+++ b/mobile/android/base/java/org/mozilla/gecko/updater/Updater.java
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.updater;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.AlarmManager;
-import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -26,19 +25,19 @@ import android.support.annotation.String
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationManagerCompat;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.FileProvider;
 import android.support.v4.net.ConnectivityManagerCompat;
 import android.util.Log;
 
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoUpdateReceiver;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.updater.UpdateServiceHelper.AutoDownloadPolicy;
 import org.mozilla.gecko.updater.UpdateServiceHelper.CheckUpdateResult;
 import org.mozilla.gecko.updater.UpdateServiceHelper.UpdateInfo;
 import org.mozilla.gecko.util.IOUtils;
 import org.mozilla.gecko.util.ProxySelector;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -230,17 +229,18 @@ public class Updater {
                 .setContentText(getString(R.string.updater_permission_text))
                 .setStyle(bigTextStyle)
                 .setAutoCancel(true)
                 .setSmallIcon(R.drawable.ic_status_logo)
                 .setColor(ContextCompat.getColor(context, R.color.rejection_red))
                 .setContentIntent(pendingIntent);
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         NotificationManagerCompat.from(context)
                 .notify(R.id.updateServicePermissionNotification, builder.build());
     }
 
     @SuppressLint("NewApi")
     private void startDownload(UpdateInfo info, int flags) {
@@ -272,17 +272,18 @@ public class Updater {
             builder.setSmallIcon(R.drawable.ic_status_logo);
             builder.setWhen(System.currentTimeMillis());
             builder.setAutoCancel(true);
             builder.setContentTitle(getString(R.string.updater_start_title));
             builder.setContentText(getString(R.string.updater_start_select));
             builder.setContentIntent(contentIntent);
 
             if (!AppConstants.Versions.preO) {
-                builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+                builder.setChannelId(NotificationHelper.getInstance(context)
+                        .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
             }
 
             notificationManager.notify(notificationId, builder.build());
 
             return;
         }
 
         File pkg = downloadUpdatePackage(info, hasFlag(flags, FLAG_OVERWRITE_EXISTING));
@@ -313,17 +314,18 @@ public class Updater {
             builder.setSmallIcon(R.drawable.ic_status_logo);
             builder.setWhen(System.currentTimeMillis());
             builder.setAutoCancel(true);
             builder.setContentTitle(getString(R.string.updater_apply_title));
             builder.setContentText(getString(R.string.updater_apply_select));
             builder.setContentIntent(contentIntent);
 
             if (!AppConstants.Versions.preO) {
-                builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+                builder.setChannelId(NotificationHelper.getInstance(context)
+                        .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
             }
 
             notificationManager.notify(notificationId, builder.build());
         }
     }
 
     private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
         URL url = null;
@@ -467,17 +469,18 @@ public class Updater {
         notifBuilder = new NotificationCompat.Builder(context);
         notifBuilder.setContentTitle(getString(R.string.updater_downloading_title))
                 .setContentText(shouldApplyImmediately ? "" : getString(R.string.updater_downloading_select))
                 .setSmallIcon(android.R.drawable.stat_sys_download)
                 .setContentIntent(contentIntent)
                 .setDeleteIntent(deleteIntent);
 
         if (!AppConstants.Versions.preO) {
-            notifBuilder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            notifBuilder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         notifBuilder.setProgress(100, 0, true);
         notificationManager.notify(notificationId, notifBuilder.build());
     }
 
     @SuppressLint("NewApi")
     private void showDownloadFailure() {
@@ -489,17 +492,18 @@ public class Updater {
         NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
         builder.setSmallIcon(R.drawable.ic_status_logo);
         builder.setWhen(System.currentTimeMillis());
         builder.setContentTitle(getString(R.string.updater_downloading_title_failed));
         builder.setContentText(getString(R.string.updater_downloading_retry));
         builder.setContentIntent(contentIntent);
 
         if (!AppConstants.Versions.preO) {
-            builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+            builder.setChannelId(NotificationHelper.getInstance(context)
+                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
         }
 
         notificationManager.notify(notificationId, builder.build());
     }
 
     private void applyUpdate(File updateFile) {
         shouldApplyImmediately = false;
 
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -887,8 +887,12 @@ See also https://bug1409261.bmoattachmen
 <!ENTITY pwa_onboarding_sumo "You can easily add this website to your Home screen to have instant access and browse faster with an app-like experience.">
 
 <!-- Used by accessibility services to identify the play/pause buttons shown in the
 Picture-in-picture mini window -->
 <!ENTITY pip_play_button_title "Play">
 <!ENTITY pip_play_button_description "Resume playing">
 <!ENTITY pip_pause_button_title "Pause">
 <!ENTITY pip_pause_button_description "Pause playing">
+
+<!-- Notification channels names -->
+<!ENTITY default_notification_channel "&brandShortName;">
+<!ENTITY mls_notification_channel "&vendorShortName; Location Service">
\ No newline at end of file
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -641,9 +641,12 @@
   <string name="pwa_add_to_launcher_badge">&pwa_add_to_launcher_badge2;</string>
   <string name="pwa_onboarding_sumo">&pwa_onboarding_sumo;</string>
   <string name="pwa_continue_to_website">&pwa_continue_to_website;</string>
 
   <string name="pip_play_button_title">&pip_play_button_title;</string>
   <string name="pip_play_button_description">&pip_play_button_description;</string>
   <string name="pip_pause_button_title">&pip_pause_button_title;</string>
   <string name="pip_pause_button_description">&pip_pause_button_description;</string>
+
+  <string name="default_notification_channel">&default_notification_channel;</string>
+  <string name="mls_notification_channel">&mls_notification_channel;</string>
 </resources>
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountNotificationManager.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountNotificationManager.java
@@ -1,32 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.sync;
 
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.support.v7.app.NotificationCompat;
 
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.Locales;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.activities.FxAccountWebFlowActivity;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.Action;
+import org.mozilla.gecko.notifications.NotificationHelper;
 
 /**
  * Abstraction that manages notifications shown or hidden for a Firefox Account.
  * <p>
  * In future, we anticipate this tracking things like:
  * <ul>
  * <li>new engines to offer to Sync;</li>
  * <li>service interruption updates;</li>
@@ -108,14 +107,15 @@ public class FxAccountNotificationManage
     builder
     .setContentTitle(title)
     .setContentText(text)
     .setSmallIcon(R.drawable.ic_status_logo)
     .setAutoCancel(true)
     .setContentIntent(pendingIntent);
 
     if (!AppConstants.Versions.preO) {
-      builder.setChannelId(GeckoApplication.getDefaultNotificationChannel().getId());
+      builder.setChannelId(NotificationHelper.getInstance(context)
+              .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
     }
 
     notificationManager.notify(notificationId, builder.build());
   }
 }
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/StumblerService.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/StumblerService.java
@@ -15,19 +15,19 @@ import android.location.Location;
 import android.os.AsyncTask;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.content.ContextCompat;
 import android.util.Log;
 import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.IntentHelper;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.mozstumbler.service.AppGlobals;
 import org.mozilla.mozstumbler.service.Prefs;
 import org.mozilla.mozstumbler.service.stumblerthread.blocklist.WifiBlockListInterface;
 import org.mozilla.mozstumbler.service.stumblerthread.datahandling.DataStorageManager;
 import org.mozilla.mozstumbler.service.stumblerthread.scanners.ScanManager;
 import org.mozilla.mozstumbler.service.uploadthread.UploadAlarmReceiver;
 import org.mozilla.mozstumbler.service.utils.PersistentIntentService;
 
@@ -139,17 +139,19 @@ public class StumblerService extends Per
         super.onCreate();
         setIntentRedelivery(true);
     }
 
     @Override
     @SuppressLint("NewApi")
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (!AppConstants.Versions.preO) {
-            final Notification notification = new NotificationCompat.Builder(this, GeckoApplication.getMLSNotificationChannel().getId())
+            final Notification notification = new NotificationCompat.Builder(this,
+                    NotificationHelper.getInstance(this)
+                            .getNotificationChannel(NotificationHelper.Channel.MLS).getId())
                     .setSmallIcon(R.drawable.ic_status_logo)
                     .setContentTitle(getString(R.string.datareporting_stumbler_notification_title))
                     .setContentIntent(createContentIntent())
                     .setOngoing(true)
                     .setShowWhen(false)
                     .setWhen(0)
                     .build();