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
--- 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 \"Open multiple links\", 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>