Bug 1304145 - 5. Implement NotificationListener in NotificationClient; r=nalexander
authorJim Chen <nchen@mozilla.com>
Thu, 22 Sep 2016 16:38:19 -0400
changeset 314944 b6b76d373a4aa56855f154ef19c44b95664ec81e
parent 314943 6b6d1a72900b72cbc7c7b2142c1e0140c6d3ab28
child 314945 4ead4233329b07cbf593a305fd8169f0f09e2233
push id82024
push usernchen@mozilla.com
push dateThu, 22 Sep 2016 20:38:38 +0000
treeherdermozilla-inbound@4ead4233329b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1304145
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1304145 - 5. Implement NotificationListener in NotificationClient; r=nalexander Provide Fennec's implementation of GeckoAppShell.NotificationListener in NotificationClient. A lot of the code was removed in an earlier patch from GeckoAppShell, so combined with this patch, we're essentially moving code from GeckoAppShell to NotificationClient.
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -132,16 +132,17 @@ public abstract class GeckoApp
     GeckoMenu.MenuPresenter,
     NativeEventListener,
     Tabs.OnTabsChangedListener,
     ViewTreeObserver.OnGlobalLayoutListener {
 
     private static final String LOGTAG = "GeckoApp";
     private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
 
+    public static final String ACTION_ALERT_CALLBACK       = "org.mozilla.gecko.ALERT_CALLBACK";
     public static final String ACTION_HOMESCREEN_SHORTCUT  = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_DEBUG                = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_LAUNCH_SETTINGS      = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_LOAD                 = "org.mozilla.gecko.LOAD";
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
     public static final String ACTION_SWITCH_TAB           = "org.mozilla.gecko.SWITCH_TAB";
 
     public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
@@ -1654,17 +1655,17 @@ public abstract class GeckoApp
                 Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED);
             }
 
             if (GeckoThread.isRunning()) {
                 geckoConnected();
             }
         }
 
-        if (GeckoAppShell.ACTION_ALERT_CALLBACK.equals(action)) {
+        if (ACTION_ALERT_CALLBACK.equals(action)) {
             processAlertCallback(intent);
         }
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
     @Override
     public void onGlobalLayout() {
         if (Versions.preJB) {
@@ -1951,17 +1952,19 @@ public abstract class GeckoApp
         if (data != null) {
             alertName = data.getQueryParameter("name");
             if (alertName == null)
                 alertName = "";
             alertCookie = data.getQueryParameter("cookie");
             if (alertCookie == null)
                 alertCookie = "";
         }
-        handleNotification(GeckoAppShell.ACTION_ALERT_CALLBACK, alertName, alertCookie);
+
+        ((NotificationClient) GeckoAppShell.getNotificationListener()).onNotificationClick(
+                alertName);
     }
 
     @Override
     protected void onNewIntent(Intent externalIntent) {
         final SafeIntent intent = new SafeIntent(externalIntent);
 
         final boolean isFirstTab = !mWasFirstTabShownAfterActivityUnhidden;
         mWasFirstTabShownAfterActivityUnhidden = true; // Reset since we'll be loading a tab.
@@ -1996,17 +1999,17 @@ public abstract class GeckoApp
                     }
                     Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
                 }
             });
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             mLayerView.loadUri(uri, GeckoView.LOAD_SWITCH_TAB);
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             mLayerView.loadUri(uri, GeckoView.LOAD_NEW_TAB);
-        } else if (GeckoAppShell.ACTION_ALERT_CALLBACK.equals(action)) {
+        } else if (ACTION_ALERT_CALLBACK.equals(action)) {
             processAlertCallback(intent);
         } else if (NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             NotificationHelper.getInstance(getApplicationContext()).handleNotificationIntent(intent);
         } else if (ACTION_LAUNCH_SETTINGS.equals(action)) {
             // Check if launched from data reporting notification.
             Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class);
             // Copy extras.
             settingsIntent.putExtras(intent.getUnsafe());
@@ -2020,17 +2023,17 @@ public abstract class GeckoApp
     }
 
     /**
      * Handles getting a URI from an intent in a way that is backwards-
      * compatible with our previous implementations.
      */
     protected String getURIFromIntent(SafeIntent intent) {
         final String action = intent.getAction();
-        if (GeckoAppShell.ACTION_ALERT_CALLBACK.equals(action) ||
+        if (ACTION_ALERT_CALLBACK.equals(action) ||
                 NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             return null;
         }
 
         return intent.getDataString();
     }
 
     protected int getOrientation() {
@@ -2383,26 +2386,16 @@ public abstract class GeckoApp
             @Override public void run() {
                 if (!isFinishing() && (Versions.preJBMR1 || !isDestroyed())) {
                     finish();
                 }
             }
         });
     }
 
-    public void handleNotification(String action, String alertName, String alertCookie) {
-        // If Gecko isn't running yet, we ignore the notification. Note that
-        // even if Gecko is running but it was restarted since the notification
-        // was created, the notification won't be handled (bug 849653).
-        if (GeckoThread.isRunning()) {
-            ((NotificationClient) GeckoAppShell.getNotificationListener()).onNotificationClick(
-                    alertName);
-        }
-    }
-
     private void checkMigrateProfile() {
         final File profileDir = getProfile().getDir();
 
         if (profileDir != null) {
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     Handler handler = new Handler();
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationClient.java
@@ -2,32 +2,107 @@
  * 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.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.LinkedList;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.GeckoApp;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoService;
+import org.mozilla.gecko.NotificationListener;
+
 /**
  * Client for posting notifications through a NotificationHandler.
  */
-public abstract class NotificationClient {
+public abstract class NotificationClient implements NotificationListener {
     private static final String LOGTAG = "GeckoNotificationClient";
 
     private volatile NotificationHandler mHandler;
     private boolean mReady;
     private final LinkedList<Runnable> mTaskQueue = new LinkedList<Runnable>();
 
+    @Override // NotificationListener
+    public void showNotification(String name, String cookie, String title,
+                                 String text, String host, String imageUrl)
+    {
+        // The intent to launch when the user clicks the expanded notification
+        final Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
+        notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
+                                        AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        // Put the strings into the intent as an URI
+        // "alert:?name=<name>&cookie=<cookie>"
+        final Uri.Builder b = new Uri.Builder();
+        final Uri dataUri = b.scheme("alert")
+                .appendQueryParameter("name", name)
+                .appendQueryParameter("cookie", cookie)
+                .build();
+        notificationIntent.setData(dataUri);
+
+        final PendingIntent clickIntent = PendingIntent.getActivity(
+                GeckoAppShell.getApplicationContext(), 0, notificationIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        add(name, imageUrl, host, title, text, clickIntent, /* closeIntent */ null);
+        GeckoAppShell.onNotificationShow(name);
+    }
+
+    @Override // NotificationListener
+    public void showPersistentNotification(String name, String cookie, String title,
+                                           String text, String host, String imageUrl,
+                                           String data)
+    {
+        final Context context = GeckoAppShell.getApplicationContext();
+
+        final PendingIntent clickIntent = PendingIntent.getService(
+                context, 0, GeckoService.getIntentToCreateServices(
+                    context, "persistent-notification-click", data),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        final PendingIntent closeIntent = PendingIntent.getService(
+                context, 0, GeckoService.getIntentToCreateServices(
+                    context, "persistent-notification-close", data),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        add(name, imageUrl, host, title, text, clickIntent, closeIntent);
+        GeckoAppShell.onNotificationShow(name);
+    }
+
+    @Override
+    public void closeNotification(String name)
+    {
+        remove(name);
+        GeckoAppShell.onNotificationClose(name);
+    }
+
+    public void onNotificationClick(String name) {
+        GeckoAppShell.onNotificationClick(name);
+
+        if (isOngoing(name)) {
+            // When clicked, keep the notification if it displays progress
+            return;
+        }
+
+        closeNotification(name);
+    }
+
     /**
      * Adds a notification.
      *
      * @see NotificationHandler#add(String, String, String, String, String, PendingIntent, PendingIntent)
      */
     public synchronized void add(final String aName, final String aImageUrl, final String aHost,
                                  final String aAlertTitle, final String aAlertText,
                                  final PendingIntent contentIntent, final PendingIntent deleteIntent) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -224,17 +224,16 @@ public class GeckoAppShell
     static public final int LINK_TYPE_USB = 2;
     static public final int LINK_TYPE_WIFI = 3;
     static public final int LINK_TYPE_WIMAX = 4;
     static public final int LINK_TYPE_2G = 5;
     static public final int LINK_TYPE_3G = 6;
     static public final int LINK_TYPE_4G = 7;
 
     public static final String PREFS_OOM_EXCEPTION = "OOMException";
-    public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ALERT_CALLBACK";
 
     /* The Android-side API: API methods that Android calls */
 
     // helper methods
     @WrapForJNI
     /* package */ static native void reportJavaCrash(Throwable exc, String stackTrace);
 
     @WrapForJNI(dispatchTo = "gecko")