| 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 337a3a997a905b558711614896dae30c57f4c9ae..a149fca21d5ad3db39536bae3edcf76d8bef53c3 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
 | 
| @@ -11,6 +11,7 @@ import android.app.PendingIntent;
 | 
|  import android.content.BroadcastReceiver;
 | 
|  import android.content.Context;
 | 
|  import android.content.Intent;
 | 
| +import android.content.SharedPreferences;
 | 
|  import android.graphics.Bitmap;
 | 
|  import android.net.Uri;
 | 
|  import android.os.Build;
 | 
| @@ -20,6 +21,8 @@ import android.support.v4.app.NotificationCompat;
 | 
|  
 | 
|  import org.chromium.base.ContextUtils;
 | 
|  import org.chromium.base.annotations.CalledByNative;
 | 
| +import org.chromium.base.annotations.JNINamespace;
 | 
| +import org.chromium.base.library_loader.LibraryLoader;
 | 
|  import org.chromium.chrome.R;
 | 
|  import org.chromium.chrome.browser.IntentHandler;
 | 
|  import org.chromium.chrome.browser.ShortcutHelper;
 | 
| @@ -30,13 +33,42 @@ import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 | 
|   *
 | 
|   * Exposes helper functions to native C++ code.
 | 
|   */
 | 
| +@JNINamespace("ntp_snippets")
 | 
|  public class ContentSuggestionsNotificationHelper {
 | 
|      private static final String NOTIFICATION_TAG = "ContentSuggestionsNotification";
 | 
|      private static final String NOTIFICATION_ID_EXTRA = "notification_id";
 | 
|  
 | 
| +    private static final String PREF_CACHED_ACTION_TAP =
 | 
| +            "ntp.content_suggestions.notification.cached_action_tap";
 | 
| +    private static final String PREF_CACHED_ACTION_DISMISSAL =
 | 
| +            "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_CONSECUTIVE_IGNORED =
 | 
| +            "ntp.content_suggestions.notification.cached_consecutive_ignored";
 | 
| +
 | 
|      private ContentSuggestionsNotificationHelper() {} // Prevent instantiation
 | 
|  
 | 
|      /**
 | 
| +     * Opens the content suggestion when notification is tapped.
 | 
| +     */
 | 
| +    public static final class OpenUrlReceiver extends BroadcastReceiver {
 | 
| +        public void onReceive(Context context, Intent intent) {
 | 
| +            openUrl(intent.getData());
 | 
| +            recordCachedActionMetric(PREF_CACHED_ACTION_TAP);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Records dismissal when notification is swiped away.
 | 
| +     */
 | 
| +    public static final class DeleteReceiver extends BroadcastReceiver {
 | 
| +        public void onReceive(Context context, Intent intent) {
 | 
| +            recordCachedActionMetric(PREF_CACHED_ACTION_DISMISSAL);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
|       * Removes the notification after a timeout period.
 | 
|       */
 | 
|      public static final class TimeoutReceiver extends BroadcastReceiver {
 | 
| @@ -44,19 +76,19 @@ public class ContentSuggestionsNotificationHelper {
 | 
|              int id = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1);
 | 
|              if (id < 0) return;
 | 
|              hideNotification(id);
 | 
| +            recordCachedActionMetric(PREF_CACHED_ACTION_HIDE_DEADLINE);
 | 
|          }
 | 
|      }
 | 
|  
 | 
| -    @CalledByNative
 | 
| -    private static void openUrl(String url) {
 | 
| +    private static void openUrl(Uri uri) {
 | 
|          Context context = ContextUtils.getApplicationContext();
 | 
| -        Intent intent = new Intent();
 | 
| -        intent.setAction(Intent.ACTION_VIEW);
 | 
| -        intent.setData(Uri.parse(url));
 | 
| -        intent.setClass(context, ChromeLauncherActivity.class);
 | 
| -        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
| -        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
 | 
| -        intent.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
 | 
| +        Intent intent = new Intent()
 | 
| +                                .setAction(Intent.ACTION_VIEW)
 | 
| +                                .setData(uri)
 | 
| +                                .setClass(context, ChromeLauncherActivity.class)
 | 
| +                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 | 
| +                                .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())
 | 
| +                                .putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
 | 
|          IntentHandler.addTrustedIntentExtras(intent);
 | 
|          context.startActivity(intent);
 | 
|      }
 | 
| @@ -81,16 +113,13 @@ public class ContentSuggestionsNotificationHelper {
 | 
|              }
 | 
|          }
 | 
|  
 | 
| -        Intent intent = new Intent()
 | 
| -                                .setAction(Intent.ACTION_VIEW)
 | 
| -                                .setData(Uri.parse(url))
 | 
| -                                .setClass(context, ChromeLauncherActivity.class)
 | 
| -                                .putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true)
 | 
| -                                .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
 | 
| +        Intent contentIntent = new Intent(context, OpenUrlReceiver.class).setData(Uri.parse(url));
 | 
| +        Intent deleteIntent = new Intent(context, DeleteReceiver.class).setData(Uri.parse(url));
 | 
|          NotificationCompat.Builder builder =
 | 
|                  new NotificationCompat.Builder(context)
 | 
|                          .setAutoCancel(true)
 | 
| -                        .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
 | 
| +                        .setContentIntent(PendingIntent.getBroadcast(context, 0, contentIntent, 0))
 | 
| +                        .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0))
 | 
|                          .setContentTitle(title)
 | 
|                          .setContentText(text)
 | 
|                          .setGroup(NOTIFICATION_TAG)
 | 
| @@ -134,4 +163,65 @@ public class ContentSuggestionsNotificationHelper {
 | 
|              manager.cancel(NOTIFICATION_TAG, 0);
 | 
|          }
 | 
|      }
 | 
| +
 | 
| +    /**
 | 
| +     * Records that an action was performed on a notification.
 | 
| +     *
 | 
| +     * Also tracks the number of consecutively-ignored notifications, resetting it on a tap or
 | 
| +     * otherwise incrementing it.
 | 
| +     *
 | 
| +     * This method may be called when the native library is not loaded. If it is loaded, the metrics
 | 
| +     * 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>)
 | 
| +     */
 | 
| +    private static void recordCachedActionMetric(String prefName) {
 | 
| +        assert prefName.startsWith("ntp.content_suggestions.notification.cached_action_");
 | 
| +
 | 
| +        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
 | 
| +        int currentValue = prefs.getInt(prefName, 0);
 | 
| +        int consecutiveIgnored = 0;
 | 
| +        if (!prefName.equals(PREF_CACHED_ACTION_TAP)) {
 | 
| +            consecutiveIgnored = 1 + prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
 | 
| +        }
 | 
| +        prefs.edit()
 | 
| +                .putInt(prefName, currentValue + 1)
 | 
| +                .putInt(PREF_CACHED_CONSECUTIVE_IGNORED, consecutiveIgnored)
 | 
| +                .apply();
 | 
| +
 | 
| +        if (LibraryLoader.isInitialized()) {
 | 
| +            flushCachedMetrics();
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Invokes nativeReceiveFlushedMetrics() with cached metrics and resets them.
 | 
| +     *
 | 
| +     * It may be called from either native or Java code, as long as the native libray is loaded.
 | 
| +     */
 | 
| +    @CalledByNative
 | 
| +    private static void flushCachedMetrics() {
 | 
| +        assert LibraryLoader.isInitialized();
 | 
| +
 | 
| +        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
 | 
| +        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 consecutiveIgnored = prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
 | 
| +
 | 
| +        if (tapCount > 0 || dismissalCount > 0 || hideDeadlineCount > 0) {
 | 
| +            nativeReceiveFlushedMetrics(tapCount, dismissalCount, hideDeadlineCount,
 | 
| +                                        consecutiveIgnored);
 | 
| +            prefs.edit()
 | 
| +                    .remove(PREF_CACHED_ACTION_TAP)
 | 
| +                    .remove(PREF_CACHED_ACTION_DISMISSAL)
 | 
| +                    .remove(PREF_CACHED_ACTION_HIDE_DEADLINE)
 | 
| +                    .remove(PREF_CACHED_CONSECUTIVE_IGNORED)
 | 
| +                    .apply();
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    private static native void nativeReceiveFlushedMetrics(
 | 
| +            int tapCount, int dismissalCount, int hideDeadlineCount, int consecutiveIgnored);
 | 
|  }
 | 
| 
 |