Bug 1075476 - Removing download notification restarts Firefox r?sebastian
MozReview-Commit-ID: 35pn1nntcsV
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -112,23 +112,16 @@
<action android:name="org.mozilla.gecko.ACTION_ALERT_CALLBACK" />
</intent-filter>
<intent-filter>
<action android:name="org.mozilla.gecko.GUEST_SESSION_INPROGRESS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
- <!-- Notification API V2 -->
- <intent-filter>
- <action android:name="@ANDROID_PACKAGE_NAME@.helperBroadcastAction" />
- <data android:scheme="moz-notification" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
-
<intent-filter>
<action android:name="org.mozilla.gecko.UPDATE"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.WEB_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
@@ -242,16 +235,27 @@
android:name="org.mozilla.gecko.notifications.WhatsNewReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" android:path="org.mozilla.gecko" />
</intent-filter>
</receiver>
+ <receiver
+ android:name="org.mozilla.gecko.NotificationReceiver"
+ android:exported="false">
+ <!-- Notification API V2 -->
+ <intent-filter>
+ <action android:name="@ANDROID_PACKAGE_NAME@.helperBroadcastAction" />
+ <data android:scheme="moz-notification" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+
#include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
#include ../search/manifests/SearchAndroidManifest_activities.xml.in
#endif
#if MOZ_CRASHREPORTER
<activity android:name="org.mozilla.gecko.CrashReporter"
android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1625,18 +1625,16 @@ public abstract class GeckoApp
if (GeckoThread.isRunning()) {
geckoConnected();
GeckoAppShell.notifyObservers("Viewport:Flush", null);
}
}
if (ACTION_ALERT_CALLBACK.equals(action)) {
processAlertCallback(intent);
- } else if (NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
- NotificationHelper.getInstance(getApplicationContext()).handleNotificationIntent(intent);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
if (Versions.preJB) {
mMainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
--- a/mobile/android/base/java/org/mozilla/gecko/NotificationHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/NotificationHelper.java
@@ -11,16 +11,17 @@ import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.mozglue.SafeIntent;
import org.mozilla.gecko.util.GeckoEventListener;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
public final class NotificationHelper implements GeckoEventListener {
@@ -40,31 +41,33 @@ public final class NotificationHelper im
private static final String PROGRESS_VALUE_ATTR = "progress_value";
private static final String PROGRESS_MAX_ATTR = "progress_max";
private static final String PROGRESS_INDETERMINATE_ATTR = "progress_indeterminate";
private static final String LIGHT_ATTR = "light";
private static final String ONGOING_ATTR = "ongoing";
private static final String WHEN_ATTR = "when";
private static final String PRIORITY_ATTR = "priority";
private static final String LARGE_ICON_ATTR = "largeIcon";
- private static final String EVENT_TYPE_ATTR = "eventType";
private static final String ACTIONS_ATTR = "actions";
private static final String ACTION_ID_ATTR = "buttonId";
private static final String ACTION_TITLE_ATTR = "title";
private static final String ACTION_ICON_ATTR = "icon";
private static final String PERSISTENT_ATTR = "persistent";
private static final String HANDLER_ATTR = "handlerKey";
private static final String COOKIE_ATTR = "cookie";
+ static final String EVENT_TYPE_ATTR = "eventType";
private static final String NOTIFICATION_SCHEME = "moz-notification";
private static final String BUTTON_EVENT = "notification-button-clicked";
private static final String CLICK_EVENT = "notification-clicked";
- private static final String CLEARED_EVENT = "notification-cleared";
private static final String CLOSED_EVENT = "notification-closed";
+ static final String CLEARED_EVENT = "notification-cleared";
+
+ static final String ORIGINAL_EXTRA_COMPONENT = "originalComponent";
private final Context mContext;
// 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 HashMap<String, String> mClearableNotifications;
private boolean mInitialized;
@@ -102,69 +105,60 @@ public final class NotificationHelper im
hideNotification(message);
}
}
public boolean isHelperIntent(Intent i) {
return i.getBooleanExtra(HELPER_NOTIFICATION, false);
}
- public void handleNotificationIntent(SafeIntent i) {
- final Uri data = i.getData();
- if (data == null) {
- Log.e(LOGTAG, "handleNotificationEvent: empty data");
- return;
- }
- final String id = data.getQueryParameter(ID_ATTR);
- final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
- if (id == null || notificationType == null) {
- Log.e(LOGTAG, "handleNotificationEvent: invalid intent parameters");
- return;
- }
+ public static void getArgsAndSendNotificationIntent(SafeIntent intent) {
+ final JSONObject args = new JSONObject();
+ final Uri data = intent.getData();
- // In case the user swiped out the notification, we empty the id set.
- if (CLEARED_EVENT.equals(notificationType)) {
- mClearableNotifications.remove(id);
- // If Gecko isn't running, we throw away events where the notification was cancelled.
- // i.e. Don't bug the user if they're just closing a bunch of notifications.
- if (!GeckoThread.isRunning()) {
- return;
- }
- }
-
- JSONObject args = new JSONObject();
-
- // The handler and cookie parameters are optional.
- final String handler = data.getQueryParameter(HANDLER_ATTR);
- final String cookie = i.getStringExtra(COOKIE_ATTR);
+ final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
try {
- args.put(ID_ATTR, id);
+ args.put(ID_ATTR, data.getQueryParameter(ID_ATTR));
args.put(EVENT_TYPE_ATTR, notificationType);
- args.put(HANDLER_ATTR, handler);
- args.put(COOKIE_ATTR, cookie);
+ args.put(HANDLER_ATTR, data.getQueryParameter(HANDLER_ATTR));
+ args.put(COOKIE_ATTR, data.getQueryParameter(COOKIE_ATTR));
if (BUTTON_EVENT.equals(notificationType)) {
final String actionName = data.getQueryParameter(ACTION_ID_ATTR);
args.put(ACTION_ID_ATTR, actionName);
}
Log.i(LOGTAG, "Send " + args.toString());
GeckoAppShell.notifyObservers("Notification:Event", args.toString());
} catch (JSONException e) {
Log.e(LOGTAG, "Error building JSON notification arguments.", e);
}
+ }
+
+ public void handleNotificationIntent(SafeIntent i) {
+ final Uri data = i.getData();
+ final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
+ final String id = data.getQueryParameter(ID_ATTR);
+ if (id == null || notificationType == null) {
+ Log.e(LOGTAG, "handleNotificationEvent: invalid intent parameters");
+ return;
+ }
+
+ getArgsAndSendNotificationIntent(i);
// If the notification was clicked, we are closing it. This must be executed after
// sending the event to js side because when the notification is canceled no event can be
// handled.
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
+ // The handler and cookie parameters are optional.
+ final String handler = data.getQueryParameter(HANDLER_ATTR);
+ final String cookie = i.getStringExtra(COOKIE_ATTR);
hideNotification(id, handler, cookie);
}
-
}
private Uri.Builder getNotificationBuilder(JSONObject message, String type) {
Uri.Builder b = new Uri.Builder();
b.scheme(NOTIFICATION_SCHEME).appendQueryParameter(EVENT_TYPE_ATTR, type);
try {
final String id = message.getString(ID_ATTR);
@@ -187,25 +181,28 @@ public final class NotificationHelper im
Intent notificationIntent = new Intent(HELPER_BROADCAST_ACTION);
final boolean ongoing = message.optBoolean(ONGOING_ATTR);
notificationIntent.putExtra(ONGOING_ATTR, ongoing);
final Uri dataUri = builder.build();
notificationIntent.setData(dataUri);
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
notificationIntent.putExtra(COOKIE_ATTR, message.optString(COOKIE_ATTR));
- notificationIntent.setClass(mContext, GeckoAppShell.getGeckoInterface().getActivity().getClass());
+
+ // All intents get routed through the notificationReceiver. That lets us bail if we don't want to start Gecko
+ final ComponentName name = new ComponentName(mContext, GeckoAppShell.getGeckoInterface().getActivity().getClass());
+ notificationIntent.putExtra(ORIGINAL_EXTRA_COMPONENT, name);
+
return notificationIntent;
}
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
Uri.Builder builder = getNotificationBuilder(message, type);
final Intent notificationIntent = buildNotificationIntent(message, builder);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
+ return PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent buildButtonClickPendingIntent(JSONObject message, JSONObject action) {
Uri.Builder builder = getNotificationBuilder(message, BUTTON_EVENT);
try {
// Action name must be in query uri, otherwise buttons pending intents
// would be collapsed.
if (action.has(ACTION_ID_ATTR)) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/NotificationReceiver.java
@@ -0,0 +1,56 @@
+/* -*- 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;
+
+import org.mozilla.gecko.mozglue.SafeIntent;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * Broadcast receiver for Notifications. Will forward them to GeckoApp (and start Gecko) if they're clicked.
+ * If they're being dismissed, it will not start Gecko, but may forward them to JS if Gecko is running.
+ * This is also the only entry point for notification intents.
+ */
+public class NotificationReceiver extends BroadcastReceiver {
+ private static final String LOGTAG = "Gecko" + NotificationReceiver.class.getSimpleName();
+
+ public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
+ if (data == null) {
+ Log.e(LOGTAG, "handleNotificationEvent: empty data");
+ return;
+ }
+
+ final String notificationType = data.getQueryParameter(NotificationHelper.EVENT_TYPE_ATTR);
+ if (notificationType == null) {
+ return;
+ }
+
+ // In case the user swiped out the notification, we empty the id set.
+ if (NotificationHelper.CLEARED_EVENT.equals(notificationType)) {
+ // If Gecko isn't running, we throw away events where the notification was cancelled.
+ // i.e. Don't bug the user if they're just closing a bunch of notifications.
+ if (GeckoThread.isRunning()) {
+ NotificationHelper.getArgsAndSendNotificationIntent(new SafeIntent(intent));
+ }
+ return;
+ }
+
+ forwardMessageToActivity(intent, context);
+ }
+
+ private void forwardMessageToActivity(final Intent intent, final Context context) {
+ final ComponentName name = intent.getExtras().getParcelable(NotificationHelper.ORIGINAL_EXTRA_COMPONENT);
+ intent.setComponent(name);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+}
\ No newline at end of file
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -474,16 +474,17 @@ gbjar.sources += ['java/org/mozilla/geck
'menu/MenuItemDefault.java',
'menu/MenuItemSwitcherLayout.java',
'menu/MenuPanel.java',
'menu/MenuPopup.java',
'MotionEventInterceptor.java',
'NotificationClient.java',
'NotificationHandler.java',
'NotificationHelper.java',
+ 'NotificationReceiver.java',
'notifications/WhatsNewReceiver.java',
'NotificationService.java',
'NSSBridge.java',
'OrderedBroadcastHelper.java',
'overlays/OverlayConstants.java',
'overlays/service/OverlayActionService.java',
'overlays/service/ShareData.java',
'overlays/service/sharemethods/AddBookmark.java',