Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1910)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java

Issue 2639553002: Track active notifications in Java (Closed)
Patch Set: FindBugs 2 Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/android/BUILD.gn ('k') | chrome/browser/android/ntp/content_suggestions_notification_helper.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
index a149fca21d5ad3db39536bae3edcf76d8bef53c3..6a0ad7b4a160c9bc7c287949d2fae433074e5cc4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
@@ -4,7 +4,6 @@
package org.chromium.chrome.browser.ntp;
-import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -14,9 +13,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Build;
import android.provider.Browser;
-import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
import org.chromium.base.ContextUtils;
@@ -27,6 +24,12 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsNotificationAction;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
/**
* Provides functionality needed for content suggestion notifications.
@@ -37,6 +40,8 @@ import org.chromium.chrome.browser.document.ChromeLauncherActivity;
public class ContentSuggestionsNotificationHelper {
private static final String NOTIFICATION_TAG = "ContentSuggestionsNotification";
private static final String NOTIFICATION_ID_EXTRA = "notification_id";
+ private static final String NOTIFICATION_CATEGORY_EXTRA = "category";
+ private static final String NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA = "id_within_category";
private static final String PREF_CACHED_ACTION_TAP =
"ntp.content_suggestions.notification.cached_action_tap";
@@ -44,9 +49,21 @@ public class ContentSuggestionsNotificationHelper {
"ntp.content_suggestions.notification.cached_action_dismissal";
private static final String PREF_CACHED_ACTION_HIDE_DEADLINE =
"ntp.content_suggestions.notification.cached_action_hide_deadline";
+ private static final String PREF_CACHED_ACTION_HIDE_EXPIRY =
+ "ntp.content_suggestions.notification.cached_action_hide_expiry";
+ private static final String PREF_CACHED_ACTION_HIDE_FRONTMOST =
+ "ntp.content_suggestions.notification.cached_action_hide_frontmost";
+ private static final String PREF_CACHED_ACTION_HIDE_DISABLED =
+ "ntp.content_suggestions.notification.cached_action_hide_disabled";
+ private static final String PREF_CACHED_ACTION_HIDE_SHUTDOWN =
+ "ntp.content_suggestions.notification.cached_action_hide_shutdown";
private static final String PREF_CACHED_CONSECUTIVE_IGNORED =
"ntp.content_suggestions.notification.cached_consecutive_ignored";
+ // Tracks which URIs there is an active notification for.
+ private static final String PREF_ACTIVE_NOTIFICATIONS =
+ "ntp.content_suggestions.notification.active";
+
private ContentSuggestionsNotificationHelper() {} // Prevent instantiation
/**
@@ -54,8 +71,11 @@ public class ContentSuggestionsNotificationHelper {
*/
public static final class OpenUrlReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
+ int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
+ String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
openUrl(intent.getData());
- recordCachedActionMetric(PREF_CACHED_ACTION_TAP);
+ recordCachedActionMetric(ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_TAP);
+ removeActiveNotification(category, idWithinCategory);
}
}
@@ -64,7 +84,11 @@ public class ContentSuggestionsNotificationHelper {
*/
public static final class DeleteReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
- recordCachedActionMetric(PREF_CACHED_ACTION_DISMISSAL);
+ int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
+ String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
+ recordCachedActionMetric(
+ ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_DISMISSAL);
+ removeActiveNotification(category, idWithinCategory);
}
}
@@ -73,10 +97,14 @@ public class ContentSuggestionsNotificationHelper {
*/
public static final class TimeoutReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
- int id = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1);
- if (id < 0) return;
- hideNotification(id);
- recordCachedActionMetric(PREF_CACHED_ACTION_HIDE_DEADLINE);
+ int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
+ String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
+ if (findActiveNotification(category, idWithinCategory) == null) {
+ return; // tapped or swiped
+ }
+
+ hideNotification(category, idWithinCategory,
+ ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_DEADLINE);
}
}
@@ -93,28 +121,28 @@ public class ContentSuggestionsNotificationHelper {
context.startActivity(intent);
}
- @TargetApi(Build.VERSION_CODES.M)
@CalledByNative
- private static void showNotification(
- String url, String title, String text, Bitmap image, long timeoutAtMillis) {
+ private static boolean showNotification(int category, String idWithinCategory, String url,
+ String title, String text, Bitmap image, long timeoutAtMillis) {
+ if (findActiveNotification(category, idWithinCategory) != null) return false;
+
// Post notification.
Context context = ContextUtils.getApplicationContext();
NotificationManager manager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- // Find an available notification ID.
- int nextId = 0;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- for (StatusBarNotification activeNotification : manager.getActiveNotifications()) {
- if (activeNotification.getTag() != NOTIFICATION_TAG) continue;
- if (activeNotification.getId() >= nextId) {
- nextId = activeNotification.getId() + 1;
- }
- }
- }
-
- Intent contentIntent = new Intent(context, OpenUrlReceiver.class).setData(Uri.parse(url));
- Intent deleteIntent = new Intent(context, DeleteReceiver.class).setData(Uri.parse(url));
+ int nextId = nextNotificationId();
+ Uri uri = Uri.parse(url);
+ Intent contentIntent =
+ new Intent(context, OpenUrlReceiver.class)
+ .setData(uri)
+ .putExtra(NOTIFICATION_CATEGORY_EXTRA, category)
+ .putExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA, idWithinCategory);
+ Intent deleteIntent =
+ new Intent(context, DeleteReceiver.class)
+ .setData(uri)
+ .putExtra(NOTIFICATION_CATEGORY_EXTRA, category)
+ .putExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA, idWithinCategory);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context)
.setAutoCancel(true)
@@ -126,42 +154,189 @@ public class ContentSuggestionsNotificationHelper {
.setLargeIcon(image)
.setSmallIcon(R.drawable.ic_chrome);
manager.notify(NOTIFICATION_TAG, nextId, builder.build());
+ addActiveNotification(new ActiveNotification(nextId, category, idWithinCategory, uri));
// Set timeout.
if (timeoutAtMillis != Long.MAX_VALUE) {
AlarmManager alarmManager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent timeoutIntent = new Intent(context, TimeoutReceiver.class)
- .setData(Uri.parse(url))
- .putExtra(NOTIFICATION_ID_EXTRA, nextId);
+ Intent timeoutIntent =
+ new Intent(context, TimeoutReceiver.class)
+ .setData(Uri.parse(url))
+ .putExtra(NOTIFICATION_ID_EXTRA, nextId)
+ .putExtra(NOTIFICATION_CATEGORY_EXTRA, category)
+ .putExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA, idWithinCategory);
alarmManager.set(AlarmManager.RTC, timeoutAtMillis,
PendingIntent.getBroadcast(
context, 0, timeoutIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
+ return true;
}
- private static void hideNotification(int id) {
+ @CalledByNative
+ private static void hideNotification(int category, String idWithinCategory, int why) {
Context context = ContextUtils.getApplicationContext();
NotificationManager manager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- manager.cancel(NOTIFICATION_TAG, id);
+ ActiveNotification activeNotification = findActiveNotification(category, idWithinCategory);
+ if (activeNotification == null) return;
+ manager.cancel(NOTIFICATION_TAG, activeNotification.mId);
+ if (removeActiveNotification(category, idWithinCategory)) {
+ recordCachedActionMetric(why);
+ }
}
- @TargetApi(Build.VERSION_CODES.M)
@CalledByNative
- private static void hideAllNotifications() {
+ private static void hideAllNotifications(int why) {
Context context = ContextUtils.getApplicationContext();
NotificationManager manager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- for (StatusBarNotification activeNotification : manager.getActiveNotifications()) {
- if (activeNotification.getTag() == NOTIFICATION_TAG) {
- manager.cancel(NOTIFICATION_TAG, activeNotification.getId());
- }
+ for (ActiveNotification activeNotification : getActiveNotifications()) {
+ manager.cancel(NOTIFICATION_TAG, activeNotification.mId);
+ recordCachedActionMetric(why);
+ }
+ }
+
+ private static class ActiveNotification {
+ final int mId;
+ final int mCategory;
+ final String mIdWithinCategory;
+ final Uri mUri;
+
+ ActiveNotification(int id, int category, String idWithinCategory, Uri uri) {
+ mId = id;
+ mCategory = category;
+ mIdWithinCategory = idWithinCategory;
+ mUri = uri;
+ }
+
+ /** Parses the fields out of a chrome://content-suggestions-notification URI */
+ static ActiveNotification fromUri(Uri notificationUri) {
+ assert notificationUri.getScheme().equals("chrome");
+ assert notificationUri.getAuthority().equals("content-suggestions-notification");
+ assert notificationUri.getQueryParameter("id") != null;
+ assert notificationUri.getQueryParameter("category") != null;
+ assert notificationUri.getQueryParameter("idWithinCategory") != null;
+ assert notificationUri.getQueryParameter("uri") != null;
+
+ return new ActiveNotification(Integer.parseInt(notificationUri.getQueryParameter("id")),
+ Integer.parseInt(notificationUri.getQueryParameter("category")),
+ notificationUri.getQueryParameter("idWithinCategory"),
+ Uri.parse(notificationUri.getQueryParameter("uri")));
+ }
+
+ /** Serializes the fields to a chrome://content-suggestions-notification URI */
+ Uri toUri() {
+ return new Uri.Builder()
+ .scheme("chrome")
+ .authority("content-suggestions-notification")
+ .appendQueryParameter("id", Integer.toString(mId))
+ .appendQueryParameter("category", Integer.toString(mCategory))
+ .appendQueryParameter("idWithinCategory", mIdWithinCategory)
+ .appendQueryParameter("uri", mUri.toString())
+ .build();
+ }
+ }
+
+ /** Returns a mutable copy of the named pref. Never returns null. */
+ private static Set<String> getMutableStringSetPreference(
+ SharedPreferences prefs, String prefName) {
+ Set<String> prefValue = prefs.getStringSet(prefName, null);
+ if (prefValue == null) {
+ return new HashSet<String>();
+ }
+ return new HashSet<String>(prefValue);
+ }
+
+ private static void addActiveNotification(ActiveNotification notification) {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications =
+ getMutableStringSetPreference(prefs, PREF_ACTIVE_NOTIFICATIONS);
+ activeNotifications.add(notification.toUri().toString());
+ prefs.edit().putStringSet(PREF_ACTIVE_NOTIFICATIONS, activeNotifications).apply();
+ }
+
+ private static boolean removeActiveNotification(int category, String idWithinCategory) {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ ActiveNotification notification = findActiveNotification(category, idWithinCategory);
+ if (notification == null) return false;
+
+ Set<String> activeNotifications =
+ getMutableStringSetPreference(prefs, PREF_ACTIVE_NOTIFICATIONS);
+ boolean result = activeNotifications.remove(notification.toUri().toString());
+ prefs.edit().putStringSet(PREF_ACTIVE_NOTIFICATIONS, activeNotifications).apply();
+ return result;
+ }
+
+ private static Collection<ActiveNotification> getActiveNotifications() {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) return Collections.emptySet();
+
+ Set<ActiveNotification> result = new HashSet<ActiveNotification>();
+ for (String serialized : activeNotifications) {
+ Uri notificationUri = Uri.parse(serialized);
+ ActiveNotification activeNotification = ActiveNotification.fromUri(notificationUri);
+ if (activeNotification != null) result.add(activeNotification);
+ }
+ return result;
+ }
+
+ /** Returns an ActiveNotification if a corresponding one is found, otherwise null. */
+ private static ActiveNotification findActiveNotification(
+ int category, String idWithinCategory) {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) return null;
+
+ for (String serialized : activeNotifications) {
+ Uri notificationUri = Uri.parse(serialized);
+ ActiveNotification activeNotification = ActiveNotification.fromUri(notificationUri);
+ if ((activeNotification != null) && (activeNotification.mCategory == category)
+ && (activeNotification.mIdWithinCategory.equals(idWithinCategory))) {
+ return activeNotification;
}
- } else {
- manager.cancel(NOTIFICATION_TAG, 0);
}
+ return null;
+ }
+
+ /** Returns a non-negative integer greater than any active notification's notification ID. */
+ private static int nextNotificationId() {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) return 0;
+
+ int nextId = 0;
+ for (String serialized : activeNotifications) {
+ Uri notificationUri = Uri.parse(serialized);
+ ActiveNotification activeNotification = ActiveNotification.fromUri(notificationUri);
+ if ((activeNotification != null) && (activeNotification.mId >= nextId)) {
+ nextId = activeNotification.mId + 1;
+ }
+ }
+ return nextId;
+ }
+
+ private static String cachedMetricNameForAction(
+ @ContentSuggestionsNotificationAction.ContentSuggestionsNotificationActionEnum
+ int action) {
+ switch (action) {
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_TAP:
+ return PREF_CACHED_ACTION_TAP;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_DISMISSAL:
+ return PREF_CACHED_ACTION_DISMISSAL;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_DEADLINE:
+ return PREF_CACHED_ACTION_HIDE_DEADLINE;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_EXPIRY:
+ return PREF_CACHED_ACTION_HIDE_EXPIRY;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_FRONTMOST:
+ return PREF_CACHED_ACTION_HIDE_FRONTMOST;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_DISABLED:
+ return PREF_CACHED_ACTION_HIDE_DISABLED;
+ case ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_SHUTDOWN:
+ return PREF_CACHED_ACTION_HIDE_SHUTDOWN;
+ }
+ return "";
}
/**
@@ -174,15 +349,18 @@ public class ContentSuggestionsNotificationHelper {
* will immediately be sent to C++. If not, it will cache them for a later call to
* flushCachedMetrics().
*
- * @param prefName The name of the action metric pref to update (<tt>PREF_CACHED_ACTION_*</tt>)
+ * @param action The action to update the pref for.
*/
- private static void recordCachedActionMetric(String prefName) {
- assert prefName.startsWith("ntp.content_suggestions.notification.cached_action_");
+ private static void recordCachedActionMetric(
+ @ContentSuggestionsNotificationAction.ContentSuggestionsNotificationActionEnum
+ int action) {
+ String prefName = cachedMetricNameForAction(action);
+ assert !prefName.isEmpty();
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
int currentValue = prefs.getInt(prefName, 0);
int consecutiveIgnored = 0;
- if (!prefName.equals(PREF_CACHED_ACTION_TAP)) {
+ if (action != ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_TAP) {
consecutiveIgnored = 1 + prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
}
prefs.edit()
@@ -208,20 +386,31 @@ public class ContentSuggestionsNotificationHelper {
int tapCount = prefs.getInt(PREF_CACHED_ACTION_TAP, 0);
int dismissalCount = prefs.getInt(PREF_CACHED_ACTION_DISMISSAL, 0);
int hideDeadlineCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_DEADLINE, 0);
+ int hideExpiryCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_EXPIRY, 0);
+ int hideFrontmostCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_FRONTMOST, 0);
+ int hideDisabledCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_DISABLED, 0);
+ int hideShutdownCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_SHUTDOWN, 0);
int consecutiveIgnored = prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
- if (tapCount > 0 || dismissalCount > 0 || hideDeadlineCount > 0) {
+ if (tapCount > 0 || dismissalCount > 0 || hideDeadlineCount > 0 || hideExpiryCount > 0
+ || hideFrontmostCount > 0 || hideDisabledCount > 0 || hideShutdownCount > 0) {
nativeReceiveFlushedMetrics(tapCount, dismissalCount, hideDeadlineCount,
- consecutiveIgnored);
+ hideExpiryCount, hideFrontmostCount, hideDisabledCount, hideShutdownCount,
+ consecutiveIgnored);
prefs.edit()
.remove(PREF_CACHED_ACTION_TAP)
.remove(PREF_CACHED_ACTION_DISMISSAL)
.remove(PREF_CACHED_ACTION_HIDE_DEADLINE)
+ .remove(PREF_CACHED_ACTION_HIDE_EXPIRY)
+ .remove(PREF_CACHED_ACTION_HIDE_FRONTMOST)
+ .remove(PREF_CACHED_ACTION_HIDE_DISABLED)
+ .remove(PREF_CACHED_ACTION_HIDE_SHUTDOWN)
.remove(PREF_CACHED_CONSECUTIVE_IGNORED)
.apply();
}
}
- private static native void nativeReceiveFlushedMetrics(
- int tapCount, int dismissalCount, int hideDeadlineCount, int consecutiveIgnored);
+ private static native void nativeReceiveFlushedMetrics(int tapCount, int dismissalCount,
+ int hideDeadlineCount, int hideExpiryCount, int hideFrontmostCount,
+ int hideDisabledCount, int hideShutdownCount, int consecutiveIgnored);
}
« no previous file with comments | « chrome/android/BUILD.gn ('k') | chrome/browser/android/ntp/content_suggestions_notification_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698