Bug 1241810 - Bug 1238087 - Update style of content notification. r=mcomella draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Thu, 25 Feb 2016 12:22:57 -0800
changeset 343935 449943603adce3e6e9d240910f78236b03fe648d
parent 343934 e5205c505298e79f1e787f6cc5e7407a84449b07
child 343936 cb943b087c76eb9042d593209e66cea8507771bd
push id13713
push users.kaspari@gmail.com
push dateWed, 23 Mar 2016 15:10:41 +0000
reviewersmcomella
bugs1241810, 1238087
milestone48.0a1
Bug 1241810 - Bug 1238087 - Update style of content notification. r=mcomella Group multiple updates into one notification and use a different style for single and multiple updates. MozReview-Commit-ID: 6PXUEcJ280P
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckAction.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/strings.xml.in
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -193,16 +193,18 @@ public class BrowserApp extends GeckoApp
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
     // Request ID for startActivityForResult.
     private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
     private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
 
+    public static final String ACTION_VIEW_MULTIPLE = AppConstants.ANDROID_PACKAGE_NAME + ".action.VIEW_MULTIPLE";
+
     @RobocopTarget
     public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
     private static final String EOL_NOTIFIED = "eol_notified";
 
     private BrowserSearch mBrowserSearch;
     private View mBrowserSearchContainer;
 
     public ViewGroup mBrowserChrome;
@@ -3686,16 +3688,17 @@ public class BrowserApp extends GeckoApp
      */
     @Override
     protected void onNewIntent(Intent intent) {
         String action = intent.getAction();
 
         final boolean isViewAction = Intent.ACTION_VIEW.equals(action);
         final boolean isBookmarkAction = GeckoApp.ACTION_HOMESCREEN_SHORTCUT.equals(action);
         final boolean isTabQueueAction = TabQueueHelper.LOAD_URLS_ACTION.equals(action);
+        final boolean isViewMultipleAction = ACTION_VIEW_MULTIPLE.equals(action);
 
         if (mInitialized && (isViewAction || isBookmarkAction)) {
             // Dismiss editing mode if the user is loading a URL from an external app.
             mBrowserToolbar.cancelEdit();
 
             // Hide firstrun-pane if the user is loading a URL from an external app.
             hideFirstrunPager(TelemetryContract.Method.NONE);
 
@@ -3726,16 +3729,24 @@ public class BrowserApp extends GeckoApp
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     openQueuedTabs();
                 }
             });
         }
 
+        // Custom intent action for opening multiple URLs at once
+        if (isViewMultipleAction) {
+            List<String> urls = intent.getStringArrayListExtra("urls");
+            if (urls != null) {
+                openUrls(urls);
+            }
+        }
+
         if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
             return;
         }
 
         // Check to see how many times the app has been launched.
         final String keyName = getPackageName() + ".feedback_launch_count";
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
@@ -3753,16 +3764,32 @@ public class BrowserApp extends GeckoApp
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feedback:Show", null));
                 }
             }
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
     }
 
+    private 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);
+
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tabs:OpenMultiple", object.toString()));
+        } catch (JSONException e) {
+            Log.e(LOGTAG, "Unable to create JSON for opening multiple URLs");
+        }
+    }
+
     private void showTabQueuePromptIfApplicable(final Intent intent) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 // We only want to show the prompt if the browser has been opened from an external url
                 if (TabQueueHelper.TAB_QUEUE_ENABLED && mInitialized
                                                      && Intent.ACTION_VIEW.equals(intent.getAction())
                                                      && !intent.getBooleanExtra(BrowserContract.SKIP_TAB_QUEUE_FLAG, false)
--- a/mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/feeds/action/CheckAction.java
@@ -9,25 +9,29 @@ 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.text.format.DateFormat;
 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 org.mozilla.gecko.util.StringUtils;
 
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 /**
  * CheckAction: Check if feeds we subscribed to have new content available.
  */
 public class CheckAction {
     private static final String LOGTAG = "FeedCheckAction";
 
@@ -39,59 +43,106 @@ public class CheckAction {
         this.storage = storage;
     }
 
     public void perform() {
         final List<FeedSubscription> subscriptions = storage.getSubscriptions();
 
         Log.d(LOGTAG, "Checking feeds for updates (" + subscriptions.size() + " feeds) ..");
 
+        List<Feed> updatedFeeds = new ArrayList<>();
+
         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);
+                updatedFeeds.add(response.feed);
             }
         }
+
+        notify(updatedFeeds);
+    }
+
+    private void notify(List<Feed> updatedFeeds) {
+        final int feedCount = updatedFeeds.size();
+
+        if (feedCount == 1) {
+            notifySingle(updatedFeeds.get(0));
+        } else if (feedCount > 1) {
+            notifyMultiple(updatedFeeds);
+        }
     }
 
-    private void notify(Feed feed) {
+    private void notifySingle(Feed feed) {
+        final String date = DateFormat.getMediumDateFormat(context).format(new Date(feed.getLastItem().getTimestamp()));
+
         NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle()
                 .bigText(feed.getLastItem().getTitle())
                 .setBigContentTitle(feed.getTitle())
-                .setSummaryText(feed.getLastItem().getURL());
+                .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()));
 
         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))
+                .setColor(ContextCompat.getColor(context, R.color.fennec_ui_orange))
                 .setContentIntent(pendingIntent)
                 .setAutoCancel(true)
                 .build();
 
         NotificationManagerCompat.from(context).notify(R.id.websiteContentNotification, notification);
     }
 
+    private void notifyMultiple(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.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);
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+
+        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())
+                .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/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -252,16 +252,24 @@
 <!-- Localization note (tab_queue_notification_text_singular2) : This is the
      text of a notification; we expect only one tab queued. -->
 <!ENTITY tab_queue_notification_text_singular2 "1 tab waiting">
 
 <!-- Localization note (tab_queue_notification_settings): This notification text is shown if a tab
      has been queued but we are missing the system permission to show an overlay. -->
 <!ENTITY tab_queue_notification_settings "To \&quot;Open multiple links\&quot;, please enable the \'Draw over other apps\' permission for &brandShortName;">
 
+<!ENTITY content_notification_summary "&brandShortName;">
+<!ENTITY content_notification_title_plural "&formatD; websites updated">
+<!ENTITY content_notification_action_settings "Notifications Setting">
+<!-- Localization note (content_notification_updated_on): &formatS; will be replaced with a medium sized version of the
+     date, depending on locale. For en_US this is for example: Feb 24, 2016. For more details see the Android developer
+     documentation for DateFormat.getMediumDateFormat(). -->
+<!ENTITY content_notification_updated_on "Updated on &formatS;">
+
 <!ENTITY pref_char_encoding "Character encoding">
 <!ENTITY pref_char_encoding_on "Show menu">
 <!ENTITY pref_char_encoding_off "Don\'t show menu">
 <!ENTITY pref_clear_private_data2 "Clear private data">
 <!-- Localization note (pref_clear_private_data_now_tablet): This action to clear private data is only shown on tablets.
      The action is shown below a header saying "Clear private data"; See pref_clear_private_data -->
 <!ENTITY pref_clear_private_data_now_tablet "Clear now">
 <!ENTITY pref_clear_on_exit_title3 "Clear private data on exit">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -280,16 +280,21 @@
   <string name="tab_queue_prompt_settings_button">&tab_queue_prompt_settings_button;</string>
   <string name="tab_queue_toast_message">&tab_queue_toast_message3;</string>
   <string name="tab_queue_toast_action">&tab_queue_toast_action;</string>
   <string name="tab_queue_notification_text_singular">&tab_queue_notification_text_singular2;</string>
   <string name="tab_queue_notification_text_plural">&tab_queue_notification_text_plural2;</string>
   <string name="tab_queue_notification_title">&tab_queue_notification_title;</string>
   <string name="tab_queue_notification_settings">&tab_queue_notification_settings;</string>
 
+  <string name="content_notification_summary">&content_notification_summary;</string>
+  <string name="content_notification_title_plural">&content_notification_title_plural;</string>
+  <string name="content_notification_action_settings">&content_notification_action_settings;</string>
+  <string name="content_notification_updated_on">&content_notification_updated_on;</string>
+
   <string name="pref_about_firefox">&pref_about_firefox;</string>
   <string name="pref_vendor_faqs">&pref_vendor_faqs;</string>
   <string name="pref_vendor_feedback">&pref_vendor_feedback;</string>
 
   <string name="pref_dialog_set_default">&pref_dialog_set_default;</string>
   <string name="pref_default">&pref_dialog_default;</string>
   <string name="pref_dialog_remove">&pref_dialog_remove;</string>