Bug 1267639 - Use BrowserAppDelegate implementation as single point for handling content notifications intents. r=grisha draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Thu, 28 Apr 2016 11:12:01 +0200
changeset 362789 9c6f93aad7f070a847b5f13ff38bbcabef684cf6
parent 362788 e0d1ed9cd56c91bb8be2659b0024b29844c1401e
child 362790 4bfb9b714b42f0a0259770e054701a3d4592aa87
push id17036
push users.kaspari@gmail.com
push dateTue, 03 May 2016 08:00:31 +0000
reviewersgrisha
bugs1267639
milestone49.0a1
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
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/feeds/ContentNotificationsDelegate.java
mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckForUpdatesAction.java
mobile/android/base/moz.build
--- 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',