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

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

Issue 2639553002: Track active notifications in Java (Closed)
Patch Set: 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..3067fc6a04e912dfe968250c76a8f27117a29dcb 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,10 @@ 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.HashSet;
+import java.util.Set;
/**
* Provides functionality needed for content suggestion notifications.
@@ -37,6 +38,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 +47,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 +69,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 +82,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 +95,15 @@ public class ContentSuggestionsNotificationHelper {
*/
public static final class TimeoutReceiver 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);
+ if (findActiveNotification(category, idWithinCategory) == null) {
+ return; // tapped or swiped
+ }
+
int id = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1);
- if (id < 0) return;
- hideNotification(id);
- recordCachedActionMetric(PREF_CACHED_ACTION_HIDE_DEADLINE);
+ hideNotification(category, idWithinCategory,
+ ContentSuggestionsNotificationAction.CONTENT_SUGGESTIONS_HIDE_DEADLINE);
}
}
@@ -93,28 +120,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 +153,176 @@ 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);
+ 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 {
+ int mId;
Bernhard Bauer 2017/01/17 15:56:55 Can you make these final?
sfiera 2017/01/17 17:32:08 Done.
+ int mCategory;
+ String mIdWithinCategory;
+ 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) {
+ if (!notificationUri.getScheme().equals("chrome")) return null;
Bernhard Bauer 2017/01/17 15:56:55 We shouldn't really get a malformed URL here, righ
sfiera 2017/01/17 17:32:08 Sure, done.
Bernhard Bauer 2017/01/17 18:01:06 Integer.parseInt() might choke though, right? Anyw
+ if (!notificationUri.getAuthority().equals("content-suggestions-notification")) {
+ return null;
}
- } else {
- manager.cancel(NOTIFICATION_TAG, 0);
+ if (notificationUri.getQueryParameter("id") == null) return null;
+ if (notificationUri.getQueryParameter("category") == null) return null;
+ if (notificationUri.getQueryParameter("idWithinCategory") == null) return null;
+ if (notificationUri.getQueryParameter("uri") == null) return 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();
+ }
+ }
+
+ private static void addActiveNotification(ActiveNotification notification) {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) activeNotifications = new HashSet<String>();
+ activeNotifications.add(notification.toUri().toString());
Bernhard Bauer 2017/01/17 15:56:55 You are not allowed to modify the set returned by
sfiera 2017/01/17 17:32:08 Done.
+ 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 = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) return false;
+ boolean result = activeNotifications.remove(notification.toUri().toString());
+ prefs.edit().putStringSet(PREF_ACTIVE_NOTIFICATIONS, activeNotifications).apply();
+ return result;
+ }
+
+ private static Set<ActiveNotification> getActiveNotifications() {
+ SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+ Set<String> activeNotifications = prefs.getStringSet(PREF_ACTIVE_NOTIFICATIONS, null);
+ if (activeNotifications == null) return new HashSet<ActiveNotification>();
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+ return null;
+ }
+
+ 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;
+ }
+
+ public static String cachedMetricNameForAction(
Bernhard Bauer 2017/01/17 15:56:55 Javadoc for public methods please.
sfiera 2017/01/17 17:32:08 Oops, shouldn't have been public.
+ @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 +335,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 +372,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