Bug 1241810 - Add background service and action to check feeds for updates. r?mcomella draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Wed, 24 Feb 2016 14:38:44 -0800
changeset 335786 ba7be9bbd3c13a4354e102106c58e3e4cd90f1cd
parent 335785 90013e1fc4e7116bd3238f102be68fce6aa8d5da
child 335787 e8e8b9d50bae2152085e209e12c81bd016f8471f
push id11875
push users.kaspari@gmail.com
push dateTue, 01 Mar 2016 15:14:45 +0000
reviewersmcomella
bugs1241810
milestone47.0a1
Bug 1241810 - Add background service and action to check feeds for updates. r?mcomella MozReview-Commit-ID: 5hD4I65OGX3
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/java/org/mozilla/gecko/feeds/FeedService.java
mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckAction.java
mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
mobile/android/base/moz.build
mobile/android/base/resources/values/ids.xml
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -355,16 +355,21 @@
         </service>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.dlc.DownloadContentService">
         </service>
 
         <service
+            android:exported="false"
+            android:name="org.mozilla.gecko.feeds.FeedService">
+        </service>
+
+        <service
           android:name="org.mozilla.gecko.telemetry.TelemetryUploadService"
           android:exported="false"/>
 
 #include ../services/manifests/FxAccountAndroidManifest_services.xml.in
 
         <service
             android:name="org.mozilla.gecko.tabqueue.TabReceivedService"
             android:exported="false" />
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/feeds/FeedService.java
@@ -0,0 +1,62 @@
+/* -*- 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.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import com.keepsafe.switchboard.SwitchBoard;
+
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.feeds.action.CheckAction;
+import org.mozilla.gecko.feeds.subscriptions.SubscriptionStorage;
+import org.mozilla.gecko.util.Experiments;
+
+/**
+ * Background service for subscribing to and checking website feeds to notify the user about updates.
+ */
+public class FeedService extends IntentService {
+    private static final String LOGTAG = "GeckoFeedService";
+
+    public static final String ACTION_CHECK = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.CHECK";
+
+    private SubscriptionStorage storage;
+
+    public FeedService() {
+        super(LOGTAG);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        storage = new SubscriptionStorage(getApplicationContext());
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (intent == null) {
+            return;
+        }
+
+        if (!SwitchBoard.isInExperiment(this, Experiments.CONTENT_NOTIFICATIONS)) {
+            Log.d(LOGTAG, "Not in content notifications experiment. Skipping.");
+            return;
+        }
+
+        switch (intent.getAction()) {
+            case ACTION_CHECK:
+                new CheckAction(this, storage).perform();
+                break;
+
+            default:
+                Log.e(LOGTAG, "Unknown action: " + intent.getAction());
+        }
+
+        storage.persistChanges();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckAction.java
@@ -0,0 +1,97 @@
+/* -*- 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.action;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.feeds.FeedFetcher;
+import org.mozilla.gecko.feeds.parser.Feed;
+import org.mozilla.gecko.feeds.subscriptions.FeedSubscription;
+import org.mozilla.gecko.feeds.subscriptions.SubscriptionStorage;
+
+import java.util.List;
+
+/**
+ * CheckAction: Check if feeds we subscribed to have new content available.
+ */
+public class CheckAction {
+    private static final String LOGTAG = "FeedCheckAction";
+
+    private Context context;
+    private SubscriptionStorage storage;
+
+    public CheckAction(Context context, SubscriptionStorage storage) {
+        this.context = context;
+        this.storage = storage;
+    }
+
+    public void perform() {
+        final List<FeedSubscription> subscriptions = storage.getSubscriptions();
+
+        Log.d(LOGTAG, "Checking feeds for updates (" + subscriptions.size() + " feeds) ..");
+
+        for (FeedSubscription subscription : subscriptions) {
+            Log.i(LOGTAG, "Checking feed: " + subscription.getFeedTitle());
+
+            FeedFetcher.FeedResponse response = fetchFeed(subscription);
+            if (response == null) {
+                continue;
+            }
+
+            if (subscription.isNewer(response)) {
+                Log.d(LOGTAG, "* Feed has changed. New item: " + response.feed.getLastItem().getTitle());
+
+                storage.updateSubscription(subscription, response);
+
+                notify(response.feed);
+            }
+        }
+    }
+
+    private void notify(Feed feed) {
+        NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle()
+                .bigText(feed.getLastItem().getTitle())
+                .setBigContentTitle(feed.getTitle())
+                .setSummaryText(feed.getLastItem().getURL());
+
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setComponent(new ComponentName(context, BrowserApp.class));
+        intent.setData(Uri.parse(feed.getLastItem().getURL()));
+
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+
+        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.link_blue))
+                .setContentIntent(pendingIntent)
+                .setAutoCancel(true)
+                .build();
+
+        NotificationManagerCompat.from(context).notify(R.id.websiteContentNotification, notification);
+    }
+
+    private FeedFetcher.FeedResponse fetchFeed(FeedSubscription subscription) {
+        return FeedFetcher.fetchAndParseFeedIfModified(
+                subscription.getFeedUrl(),
+                subscription.getETag(),
+                subscription.getLastModified()
+        );
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
@@ -30,16 +30,19 @@ public class Experiments {
     public static final String ONBOARDING2_B = "onboarding2-b"; // 4 static Feature slides
     public static final String ONBOARDING2_C = "onboarding2-c"; // 4 static + 1 clickable (Data saving) Feature slides
 
     public static final String PREF_ONBOARDING_VERSION = "onboarding_version";
 
     // Show search mode (instead of home panels) when tapping on urlbar if there is a search term in the urlbar.
     public static final String SEARCH_TERM = "search-term";
 
+    // Subscribe to known, bookmarked sites and show a notification if new content is available.
+    public static final String CONTENT_NOTIFICATIONS = "content-notifications";
+
     private static volatile Boolean disabled = null;
 
     /**
      * Determines whether Switchboard is disabled by the MOZ_DISABLE_SWITCHBOARD
      * environment variable. We need to read this value from the intent string
      * extra because environment variables from our test harness aren't set
      * until Gecko is loaded, and we need to know this before then.
      *
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -270,17 +270,19 @@ gbjar.sources += ['java/org/mozilla/geck
     'favicons/decoders/FaviconDecoder.java',
     'favicons/decoders/ICODecoder.java',
     'favicons/decoders/IconDirectoryEntry.java',
     'favicons/decoders/LoadFaviconResult.java',
     'favicons/Favicons.java',
     'favicons/LoadFaviconTask.java',
     'favicons/OnFaviconLoadedListener.java',
     'favicons/RemoteFavicon.java',
+    'feeds/action/CheckAction.java',
     'feeds/FeedFetcher.java',
+    'feeds/FeedService.java',
     'feeds/parser/Feed.java',
     'feeds/parser/Item.java',
     'feeds/parser/SimpleFeedParser.java',
     'feeds/subscriptions/FeedSubscription.java',
     'feeds/subscriptions/SubscriptionStorage.java',
     'FilePicker.java',
     'FilePickerResultHandler.java',
     'FindInPageBar.java',
--- a/mobile/android/base/resources/values/ids.xml
+++ b/mobile/android/base/resources/values/ids.xml
@@ -12,10 +12,11 @@
     <item type="id" name="menu_items"/>
     <item type="id" name="menu_margin"/>
     <item type="id" name="recycler_view_click_support" />
     <item type="id" name="range_list"/>
     <item type="id" name="pref_header_general"/>
     <item type="id" name="pref_header_privacy"/>
     <item type="id" name="pref_header_search"/>
     <item type="id" name="updateServicePermissionNotification" />
+    <item type="id" name="websiteContentNotification" />
 
 </resources>