Bug 1267639 - Use BrowserAppDelegate implementation as single point for handling content notifications intents. r=grisha
This patch will create a single Intent action for all content notifications. The intent will be handled by
ContentNotificationsDelegate exclusively.
MozReview-Commit-ID: 5UVVanLLd32
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -26,16 +26,17 @@ import org.mozilla.gecko.db.BrowserContr
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.SuggestedSites;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.distribution.DistributionStoreCallback;
import org.mozilla.gecko.dlc.DownloadContentService;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
+import org.mozilla.gecko.feeds.ContentNotificationsDelegate;
import org.mozilla.gecko.feeds.FeedService;
import org.mozilla.gecko.feeds.action.CheckForUpdatesAction;
import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.home.BrowserSearch;
@@ -302,17 +303,18 @@ public class BrowserApp extends GeckoApp
// both the web content and the HomePager will be hidden. This flag is used to prevent the
// race by determining if the web content should be hidden at the animation's end.
private boolean mHideWebContentOnAnimationEnd;
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
(BrowserAppDelegate) new AddToHomeScreenPromotion(),
- (BrowserAppDelegate) new ScreenshotDelegate()
+ (BrowserAppDelegate) new ScreenshotDelegate(),
+ (BrowserAppDelegate) new ContentNotificationsDelegate()
));
@NonNull
private SearchEngineManager searchEngineManager; // Contains reference to Context - DO NOT LEAK!
@Override
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
final View view;
@@ -1045,26 +1047,16 @@ public class BrowserApp extends GeckoApp
}
}
private void openMultipleTabsFromIntent(final Intent intent) {
final List<String> urls = intent.getStringArrayListExtra("urls");
if (urls != null) {
openUrls(urls);
}
-
- // Launched from a "content notification"
- if (intent.hasExtra(CheckForUpdatesAction.EXTRA_CONTENT_NOTIFICATION)) {
- Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(this));
-
- Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.NOTIFICATION, "content_update");
- Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "content_update");
-
- Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(this));
- }
}
@Override
public void onResume() {
super.onResume();
// Needed for Adjust to get accurate session measurements
AdjustConstants.getAdjustHelper().onResume();
@@ -3865,17 +3857,17 @@ public class BrowserApp extends GeckoApp
GeckoAppShell.notifyObservers("Feedback:Show", null);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
- private void openUrls(List<String> urls) {
+ public void openUrls(List<String> urls) {
try {
JSONArray array = new JSONArray();
for (String url : urls) {
array.put(url);
}
JSONObject object = new JSONObject();
object.put("urls", array);
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/feeds/ContentNotificationsDelegate.java
@@ -0,0 +1,65 @@
+/* -*- 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.feeds;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.BrowserAppDelegate;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+
+import java.util.List;
+
+/**
+ * BrowserAppDelegate implementation that takes care of handling intents from content notifications.
+ */
+public class ContentNotificationsDelegate extends BrowserAppDelegate {
+ // The application is opened from a content notification
+ public static final String ACTION_CONTENT_NOTIFICATION = AppConstants.ANDROID_PACKAGE_NAME + ".action.CONTENT_NOTIFICATION";
+
+ public static final String EXTRA_URLS = "urls";
+
+ private static final String TELEMETRY_EXTRA_CONTENT_UPDATE = "content_update";
+
+ @Override
+ public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
+ final Intent intent = browserApp.getIntent();
+
+ if (savedInstanceState != null) {
+ // This activity is getting restored: We do not want to handle the URLs in the Intent again. The browser
+ // will take care of restoring the tabs we already created.
+ return;
+ }
+
+ if (intent != null && ACTION_CONTENT_NOTIFICATION.equals(intent.getAction())) {
+ openURLsFromIntent(browserApp, intent);
+ }
+ }
+
+ @Override
+ public void onNewIntent(BrowserApp browserApp, Intent intent) {
+ if (intent != null && ACTION_CONTENT_NOTIFICATION.equals(intent.getAction())) {
+ openURLsFromIntent(browserApp, intent);
+ }
+ }
+
+ private void openURLsFromIntent(BrowserApp browserApp, final Intent intent) {
+ final List<String> urls = intent.getStringArrayListExtra(EXTRA_URLS);
+ if (urls != null) {
+ browserApp.openUrls(urls);
+ }
+
+ Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(browserApp));
+
+ Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.NOTIFICATION, TELEMETRY_EXTRA_CONTENT_UPDATE);
+ Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, TELEMETRY_EXTRA_CONTENT_UPDATE);
+
+ Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(browserApp));
+ }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckForUpdatesAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckForUpdatesAction.java
@@ -16,46 +16,47 @@ import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.text.format.DateFormat;
import org.json.JSONException;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.UrlAnnotations;
+import org.mozilla.gecko.feeds.ContentNotificationsDelegate;
import org.mozilla.gecko.feeds.FeedFetcher;
import org.mozilla.gecko.feeds.FeedService;
import org.mozilla.gecko.feeds.parser.Feed;
import org.mozilla.gecko.feeds.subscriptions.FeedSubscription;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.util.StringUtils;
+import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* CheckForUpdatesAction: Check if feeds we subscribed to have new content available.
*/
public class CheckForUpdatesAction extends FeedAction {
/**
* This extra will be added to Intents fired by the notification.
*/
public static final String EXTRA_CONTENT_NOTIFICATION = "content-notification";
- private static final String LOGTAG = "FeedCheckAction";
-
- private Context context;
+ private final Context context;
public CheckForUpdatesAction(Context context) {
this.context = context;
}
@Override
public void perform(BrowserDB browserDB, Intent intent) {
final UrlAnnotations urlAnnotations = browserDB.getUrlAnnotations();
@@ -158,76 +159,81 @@ public class CheckForUpdatesAction exten
Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(context));
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.NOTIFICATION, "content_update");
Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, FeedService.getEnabledExperiment(context));
}
private void showNotificationForSingleUpdate(Feed feed) {
final String date = DateFormat.getMediumDateFormat(context).format(new Date(feed.getLastItem().getTimestamp()));
- NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle()
+ final NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle()
.bigText(feed.getLastItem().getTitle())
.setBigContentTitle(feed.getTitle())
.setSummaryText(context.getString(R.string.content_notification_updated_on, date));
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setComponent(new ComponentName(context, BrowserApp.class));
- intent.setData(Uri.parse(feed.getLastItem().getURL()));
- intent.putExtra(EXTRA_CONTENT_NOTIFICATION, true);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, createOpenIntent(feed), PendingIntent.FLAG_UPDATE_CURRENT);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
-
- Notification notification = new NotificationCompat.Builder(context)
+ final Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle(feed.getTitle())
.setContentText(feed.getLastItem().getTitle())
.setStyle(style)
.setColor(ContextCompat.getColor(context, R.color.fennec_ui_orange))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.addAction(createNotificationSettingsAction())
.build();
NotificationManagerCompat.from(context).notify(R.id.websiteContentNotification, notification);
}
private void showNotificationForMultipleUpdates(List<Feed> feeds) {
- final ArrayList<String> urls = new ArrayList<>();
-
final NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
for (Feed feed : feeds) {
- final String url = feed.getLastItem().getURL();
-
- inboxStyle.addLine(StringUtils.stripScheme(url, StringUtils.UrlFlags.STRIP_HTTPS));
- urls.add(url);
+ inboxStyle.addLine(StringUtils.stripScheme(feed.getLastItem().getURL(), StringUtils.UrlFlags.STRIP_HTTPS));
}
inboxStyle.setSummaryText(context.getString(R.string.content_notification_summary));
- Intent intent = new Intent(context, BrowserApp.class);
- intent.setAction(BrowserApp.ACTION_VIEW_MULTIPLE);
- intent.putStringArrayListExtra("urls", urls);
- intent.putExtra(EXTRA_CONTENT_NOTIFICATION, true);
-
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, createOpenIntent(feeds), PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle(context.getString(R.string.content_notification_title_plural, feeds.size()))
.setContentText(context.getString(R.string.content_notification_summary))
.setStyle(inboxStyle)
.setColor(ContextCompat.getColor(context, R.color.fennec_ui_orange))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setNumber(feeds.size())
.addAction(createNotificationSettingsAction())
.build();
NotificationManagerCompat.from(context).notify(R.id.websiteContentNotification, notification);
}
+ private Intent createOpenIntent(Feed feed) {
+ final List<Feed> feeds = new ArrayList<>();
+ feeds.add(feed);
+
+ return createOpenIntent(feeds);
+ }
+
+ private Intent createOpenIntent(List<Feed> feeds) {
+ final ArrayList<String> urls = new ArrayList<>();
+ for (Feed feed : feeds) {
+ urls.add(feed.getLastItem().getURL());
+ }
+
+ final Intent intent = new Intent(context, BrowserApp.class);
+ intent.setAction(ContentNotificationsDelegate.ACTION_CONTENT_NOTIFICATION);
+ intent.putStringArrayListExtra(ContentNotificationsDelegate.EXTRA_URLS, urls);
+
+ return intent;
+ }
+
private NotificationCompat.Action createNotificationSettingsAction() {
final Intent intent = new Intent(GeckoApp.ACTION_LAUNCH_SETTINGS);
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
intent.putExtra(EXTRA_CONTENT_NOTIFICATION, true);
GeckoPreferences.setResourceToOpen(intent, "preferences_notifications");
PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -280,16 +280,17 @@ gbjar.sources += ['java/org/mozilla/geck
'favicons/OnFaviconLoadedListener.java',
'favicons/RemoteFavicon.java',
'feeds/action/CheckForUpdatesAction.java',
'feeds/action/EnrollSubscriptionsAction.java',
'feeds/action/FeedAction.java',
'feeds/action/SetupAlarmsAction.java',
'feeds/action/SubscribeToFeedAction.java',
'feeds/action/WithdrawSubscriptionsAction.java',
+ 'feeds/ContentNotificationsDelegate.java',
'feeds/FeedAlarmReceiver.java',
'feeds/FeedFetcher.java',
'feeds/FeedService.java',
'feeds/knownsites/KnownSite.java',
'feeds/knownsites/KnownSiteBlogger.java',
'feeds/knownsites/KnownSiteMedium.java',
'feeds/knownsites/KnownSiteTumblr.java',
'feeds/knownsites/KnownSiteWordpress.java',