blob: 9e5b459f1a6c702530e33dcac72c4edb1e073e33 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.notifications;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.StrictMode;
import android.os.SystemClock;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import org.chromium.base.CommandLine;
import org.chromium.base.FieldTrialList;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences;
import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
import org.chromium.chrome.browser.preferences.website.SiteSettingsCategory;
import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/**
* Provides the ability for the NotificationUIManagerAndroid to talk to the Android platform
* notification manager.
*
* This class should only be used on the UI thread.
*/
public class NotificationUIManager {
private static final String TAG = NotificationUIManager.class.getSimpleName();
// We always use the same integer id when showing and closing notifications. The notification
// tag is always set, which is a safe and sufficient way of identifying a notification, so the
// integer id is not needed anymore except it must not vary in an uncontrolled way.
@VisibleForTesting static final int PLATFORM_ID = -1;
// Prefix for platform tags generated by this class. This allows us to verify when reading a tag
// that it was set by us.
private static final String PLATFORM_TAG_PREFIX = NotificationUIManager.class.getSimpleName();
private static final int NOTIFICATION_ICON_BG_COLOR = 0xFF969696;
private static final int NOTIFICATION_TEXT_SIZE_DP = 28;
// We always use the same request code for pending intents. We use other ways to force
// uniqueness of pending intents when necessary.
private static final int PENDING_INTENT_REQUEST_CODE = 0;
private static NotificationUIManager sInstance;
private static NotificationManagerProxy sNotificationManagerOverride;
private final long mNativeNotificationManager;
private final Context mAppContext;
private final NotificationManagerProxy mNotificationManager;
@VisibleForTesting public RoundedIconGenerator mIconGenerator;
private final int mLargeIconWidthPx;
private final int mLargeIconHeightPx;
private final float mDensity;
private long mLastNotificationClickMs = 0L;
/**
* Creates a new instance of the NotificationUIManager.
*
* @param nativeNotificationManager Instance of the NotificationUIManagerAndroid class.
* @param context Application context for this instance of Chrome.
*/
@CalledByNative
private static NotificationUIManager create(long nativeNotificationManager, Context context) {
if (sInstance != null) {
throw new IllegalStateException("There must only be a single NotificationUIManager.");
}
sInstance = new NotificationUIManager(nativeNotificationManager, context);
return sInstance;
}
/**
* Returns the current instance of the NotificationUIManager.
*
* @return The instance of the NotificationUIManager, if any.
*/
@Nullable
@VisibleForTesting
static NotificationUIManager getInstanceForTests() {
return sInstance;
}
/**
* Overrides the notification manager which is to be used for displaying Notifications on the
* Android framework. Should only be used for testing. Tests are expected to clean up after
* themselves by setting this to NULL again.
*
* @param proxy The notification manager instance to use instead of the system's.
*/
@VisibleForTesting
public static void overrideNotificationManagerForTesting(
NotificationManagerProxy notificationManager) {
sNotificationManagerOverride = notificationManager;
}
private NotificationUIManager(long nativeNotificationManager, Context context) {
mNativeNotificationManager = nativeNotificationManager;
mAppContext = context.getApplicationContext();
if (sNotificationManagerOverride != null) {
mNotificationManager = sNotificationManagerOverride;
} else {
mNotificationManager = new NotificationManagerProxyImpl(
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
}
Resources resources = mAppContext.getResources();
mLargeIconWidthPx =
resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
mLargeIconHeightPx =
resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
mDensity = resources.getDisplayMetrics().density;
}
/**
* Marks the current instance as being freed, allowing for a new NotificationUIManager
* object to be initialized.
*/
@CalledByNative
private void destroy() {
assert sInstance == this;
sInstance = null;
}
/**
* Invoked by the NotificationService when a Notification intent has been received. There may
* not be an active instance of the NotificationUIManager at this time, so inform the native
* side through a static method, initializing the manager if needed.
*
* @param intent The intent as received by the Notification service.
* @return Whether the event could be handled by the native Notification manager.
*/
public static boolean dispatchNotificationEvent(Intent intent) {
if (sInstance == null) {
nativeInitializeNotificationUIManager();
if (sInstance == null) {
Log.e(TAG, "Unable to initialize the native NotificationUIManager.");
return false;
}
}
long persistentNotificationId =
intent.getLongExtra(NotificationConstants.EXTRA_PERSISTENT_NOTIFICATION_ID, -1);
String origin = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN);
String profileId =
intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID);
boolean incognito = intent.getBooleanExtra(
NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_INCOGNITO, false);
String tag = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_TAG);
Log.i(TAG, "Dispatching notification event to native: " + persistentNotificationId);
if (NotificationConstants.ACTION_CLICK_NOTIFICATION.equals(intent.getAction())) {
int actionIndex = intent.getIntExtra(
NotificationConstants.EXTRA_NOTIFICATION_INFO_ACTION_INDEX, -1);
sInstance.onNotificationClicked(
persistentNotificationId, origin, profileId, incognito, tag, actionIndex);
return true;
} else if (NotificationConstants.ACTION_CLOSE_NOTIFICATION.equals(intent.getAction())) {
// Notification deleteIntent is executed only "when the notification is explicitly
// dismissed by the user, either with the 'Clear All' button or by swiping it away
// individually" (though a third-party NotificationListenerService may also trigger it).
sInstance.onNotificationClosed(
persistentNotificationId, origin, profileId, incognito, tag, true /* byUser */);
return true;
}
Log.e(TAG, "Unrecognized Notification action: " + intent.getAction());
return false;
}
/**
* Launches the notifications preferences screen. If the received intent indicates it came
* from the gear button on a flipped notification, this launches the site specific preferences
* screen.
*
* @param context The context that received the intent.
* @param incomingIntent The received intent.
*/
public static void launchNotificationPreferences(Context context, Intent incomingIntent) {
// Use the application context because it lives longer. When using he given context, it
// may be stopped before the preferences intent is handled.
Context applicationContext = context.getApplicationContext();
// If we can read an origin from the intent, use it to open the settings screen for that
// origin.
String origin = getOriginFromTag(
incomingIntent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_TAG));
boolean launchSingleWebsitePreferences = origin != null;
String fragmentName = launchSingleWebsitePreferences
? SingleWebsitePreferences.class.getName()
: SingleCategoryPreferences.class.getName();
Intent preferencesIntent =
PreferencesLauncher.createIntentForSettingsPage(applicationContext, fragmentName);
Bundle fragmentArguments;
if (launchSingleWebsitePreferences) {
// Record that the user has clicked on the [Site Settings] button.
RecordUserAction.record("Notifications.ShowSiteSettings");
// All preferences for a specific origin.
fragmentArguments = SingleWebsitePreferences.createFragmentArgsForSite(origin);
} else {
// Notification preferences for all origins.
fragmentArguments = new Bundle();
fragmentArguments.putString(SingleCategoryPreferences.EXTRA_CATEGORY,
SiteSettingsCategory.CATEGORY_NOTIFICATIONS);
fragmentArguments.putString(SingleCategoryPreferences.EXTRA_TITLE,
applicationContext.getResources().getString(
R.string.push_notifications_permission_title));
}
preferencesIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArguments);
// We need to ensure that no existing preference tasks are being re-used in order for the
// new activity to appear on top.
preferencesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
applicationContext.startActivity(preferencesIntent);
}
/**
* Returns a bogus Uri used to make each intent unique according to Intent#filterEquals.
* Without this, the pending intents derived from the intent may be reused, because extras are
* not taken into account for the filterEquals comparison.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin to whom the notification belongs.
* @param actionIndex The zero-based index of the action button, or -1 if not applicable.
*/
private Uri makeIntentData(long persistentNotificationId, String origin, int actionIndex) {
return Uri.parse(origin).buildUpon().fragment(
persistentNotificationId + "," + actionIndex).build();
}
/**
* Returns the PendingIntent for completing |action| on the notification identified by the data
* in the other parameters.
*
* @param action The action this pending intent will represent.
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin to whom the notification belongs.
* @param tag The tag of the notification. May be NULL.
* @param actionIndex The zero-based index of the action button, or -1 if not applicable.
*/
private PendingIntent makePendingIntent(String action, long persistentNotificationId,
String origin, String profileId, boolean incognito, @Nullable String tag,
int actionIndex) {
Uri intentData = makeIntentData(persistentNotificationId, origin, actionIndex);
Intent intent = new Intent(action, intentData);
intent.setClass(mAppContext, NotificationService.Receiver.class);
intent.putExtra(NotificationConstants.EXTRA_PERSISTENT_NOTIFICATION_ID,
persistentNotificationId);
intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN, origin);
intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID, profileId);
intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_INCOGNITO, incognito);
intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_TAG, tag);
intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ACTION_INDEX, actionIndex);
return PendingIntent.getBroadcast(mAppContext, PENDING_INTENT_REQUEST_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Generates the tag to be passed to the notification manager.
*
* If the generated tag is the same as that of a previous notification, a new notification shown
* with this tag will replace it.
*
* If the input tag is not empty the output is: PREFIX + SEPARATOR + ORIGIN + SEPARATOR + TAG.
* This output will be the same for notifications from the same origin that have the same input
* tag.
*
* If the input tag is empty the output is PREFIX + SEPARATOR + ORIGIN + SEPARATOR +
* NOTIFICATION_ID.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin for which the notification is shown.
* @param tag A string identifier for this notification.
* @return The generated platform tag.
*/
private static String makePlatformTag(long persistentNotificationId, String origin,
@Nullable String tag) {
// The given tag may contain the separator character, so add it last to make reading the
// preceding origin token reliable. If no tag was specified (it is the default empty
// string), make the platform tag unique by appending the notification id.
StringBuilder builder = new StringBuilder();
builder.append(PLATFORM_TAG_PREFIX)
.append(NotificationConstants.NOTIFICATION_TAG_SEPARATOR)
.append(origin)
.append(NotificationConstants.NOTIFICATION_TAG_SEPARATOR);
if (TextUtils.isEmpty(tag)) {
builder.append(persistentNotificationId);
} else {
builder.append(tag);
}
return builder.toString();
}
/**
* Attempts to extract an origin from the tag extra in the given intent.
*
* See {@link #makePlatformTag} for details about the format of the tag.
*
* @param tag The tag from the intent extra. May be null.
* @return The origin string. Returns null if there was no tag extra in the given intent, or if
* the tag value did not match the expected format.
*/
@Nullable
@VisibleForTesting
static String getOriginFromTag(@Nullable String tag) {
// If the user touched the settings cog on a flipped notification originating from this
// class, there will be a notification tag extra in a specific format. From the tag we can
// read the origin of the notification.
if (tag == null || !tag.startsWith(PLATFORM_TAG_PREFIX)) return null;
String[] parts = tag.split(NotificationConstants.NOTIFICATION_TAG_SEPARATOR);
assert parts.length >= 3;
try {
URI uri = new URI(parts[1]);
if (uri.getHost() != null) return parts[1];
} catch (URISyntaxException e) {
Log.e(TAG, "Expected to find a valid url in the notification tag extra.", e);
return null;
}
return null;
}
/**
* Generates the notfiication defaults from vibrationPattern's size and silent.
*
* Use the system's default ringtone, vibration and indicator lights unless the notification
* has been marked as being silent.
* If a vibration pattern is set, the notification should use the provided pattern
* rather than the defaulting to system settings.
*
* @param vibrationPatternLength Vibration pattern's size for the Notification.
* @param silent Whether the default sound, vibration and lights should be suppressed.
* @return The generated notification's default value.
*/
@VisibleForTesting
static int makeDefaults(int vibrationPatternLength, boolean silent) {
assert !silent || vibrationPatternLength == 0;
if (silent) return 0;
int defaults = Notification.DEFAULT_ALL;
if (vibrationPatternLength > 0) {
defaults &= ~Notification.DEFAULT_VIBRATE;
}
return defaults;
}
/**
* Generates the vibration pattern used in Android notification.
*
* Android takes a long array where the first entry indicates the number of milliseconds to wait
* prior to starting the vibration, whereas Chrome follows the syntax of the Web Vibration API.
*
* @param vibrationPattern Vibration pattern following the Web Vibration API syntax.
* @return Vibration pattern following the Android syntax.
*/
@VisibleForTesting
static long[] makeVibrationPattern(int[] vibrationPattern) {
long[] pattern = new long[vibrationPattern.length + 1];
for (int i = 0; i < vibrationPattern.length; ++i) {
pattern[i + 1] = vibrationPattern[i];
}
return pattern;
}
/**
* Displays a notification with the given details.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin Full text of the origin, including the protocol, owning this notification.
* @param profileId Id of the profile that showed the notification.
* @param incognito if the session of the profile is an off the record one.
* @param tag A string identifier for this notification. If the tag is not empty, the new
* notification will replace the previous notification with the same tag and origin,
* if present. If no matching previous notification is present, the new one will just
* be added.
* @param title Title to be displayed in the notification.
* @param body Message to be displayed in the notification. Will be trimmed to one line of
* text by the Android notification system.
* @param icon Icon to be displayed in the notification. Valid Bitmap icons will be scaled to
* the platforms, whereas a default icon will be generated for invalid Bitmaps.
* @param vibrationPattern Vibration pattern following the Web Vibration syntax.
* @param timestamp The timestamp of the event for which the notification is being shown.
* @param renotify Whether the sound, vibration, and lights should be replayed if the
* notification is replacing another notification.
* @param silent Whether the default sound, vibration and lights should be suppressed.
* @param actionTitles Titles of actions to display alongside the notification.
* @see https://developer.android.com/reference/android/app/Notification.html
*/
@CalledByNative
private void displayNotification(long persistentNotificationId, String origin, String profileId,
boolean incognito, String tag, String title, String body, Bitmap icon,
int[] vibrationPattern, long timestamp, boolean renotify, boolean silent,
String[] actionTitles) {
Resources res = mAppContext.getResources();
// Record whether it's known whether notifications can be shown to the user at all.
RecordHistogram.recordEnumeratedHistogram(
"Notifications.AppNotificationStatus",
NotificationSystemStatusUtil.determineAppNotificationStatus(mAppContext),
NotificationSystemStatusUtil.APP_NOTIFICATIONS_STATUS_BOUNDARY);
// Set up a pending intent for going to the settings screen for |origin|.
Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage(
mAppContext, SingleWebsitePreferences.class.getName());
settingsIntent.setData(
makeIntentData(persistentNotificationId, origin, -1 /* actionIndex */));
settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS,
SingleWebsitePreferences.createFragmentArgsForSite(origin));
PendingIntent pendingSettingsIntent = PendingIntent.getActivity(mAppContext,
PENDING_INTENT_REQUEST_CODE, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent clickIntent = makePendingIntent(
NotificationConstants.ACTION_CLICK_NOTIFICATION, persistentNotificationId, origin,
profileId, incognito, tag, -1 /* actionIndex */);
PendingIntent closeIntent = makePendingIntent(
NotificationConstants.ACTION_CLOSE_NOTIFICATION, persistentNotificationId, origin,
profileId, incognito, tag, -1 /* actionIndex */);
NotificationBuilderBase notificationBuilder =
createNotificationBuilder()
.setTitle(title)
.setBody(body)
.setLargeIcon(ensureNormalizedIcon(icon, origin))
.setSmallIcon(R.drawable.ic_chrome)
.setContentIntent(clickIntent)
.setDeleteIntent(closeIntent)
.setTicker(createTickerText(title, body))
.setTimestamp(timestamp)
.setRenotify(renotify)
.setOrigin(UrlUtilities.formatUrlForSecurityDisplay(
origin, false /* showScheme */));
for (int actionIndex = 0; actionIndex < actionTitles.length; actionIndex++) {
notificationBuilder.addAction(0 /* actionIcon */, actionTitles[actionIndex],
makePendingIntent(NotificationConstants.ACTION_CLICK_NOTIFICATION,
persistentNotificationId, origin, profileId,
incognito, tag, actionIndex));
}
// If action buttons are displayed, there isn't room for the full Site Settings button
// label and icon, so abbreviate it. This has the unfortunate side-effect of unnecessarily
// abbreviating it on Android Wear also (crbug.com/576656). If custom layouts are enabled,
// the label and icon provided here only affect Android Wear, so don't abbreviate them.
boolean abbreviateSiteSettings = actionTitles.length > 0 && !useCustomLayouts();
int settingsIconId = abbreviateSiteSettings ? 0 : R.drawable.settings_cog;
CharSequence settingsTitle = abbreviateSiteSettings
? res.getString(R.string.notification_site_settings_button)
: res.getString(R.string.page_info_site_settings_button);
// If the settings button is displayed together with the other buttons it has to be the last
// one, so add it after the other actions.
notificationBuilder.addSettingsAction(settingsIconId, settingsTitle, pendingSettingsIntent);
notificationBuilder.setDefaults(makeDefaults(vibrationPattern.length, silent));
if (vibrationPattern.length > 0) {
notificationBuilder.setVibrate(makeVibrationPattern(vibrationPattern));
}
String platformTag = makePlatformTag(persistentNotificationId, origin, tag);
// Temporarily allowing disk access. TODO: Fix. See http://crbug.com/577185
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
StrictMode.allowThreadDiskWrites();
try {
long time = SystemClock.elapsedRealtime();
mNotificationManager.notify(platformTag, PLATFORM_ID, notificationBuilder.build());
RecordHistogram.recordTimesHistogram("Android.StrictMode.NotificationUIBuildTime",
SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
private NotificationBuilderBase createNotificationBuilder() {
if (useCustomLayouts()) {
return new CustomNotificationBuilder(mAppContext);
}
return new StandardNotificationBuilder(mAppContext);
}
/**
* Creates the ticker text for a notification having |title| and |body|. The notification's
* title will be printed in bold, followed by the text of the body.
*
* @param title Title of the notification.
* @param body Textual contents of the notification.
* @return A character sequence containing the ticker's text.
*/
private CharSequence createTickerText(String title, String body) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append(title);
spannableStringBuilder.append("\n");
spannableStringBuilder.append(body);
// Mark the title of the notification as being bold.
spannableStringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
return spannableStringBuilder;
}
/**
* Ensures the availability of an icon for the notification.
*
* If |icon| is a valid, non-empty Bitmap, the bitmap will be scaled to be of an appropriate
* size for the current Android device. Otherwise, a default icon will be created based on the
* origin the notification is being displayed for.
*
* @param icon The developer-provided icon they intend to use for the notification.
* @param origin The origin the notification is being displayed for.
* @return An appropriately sized icon to use for the notification.
*/
@VisibleForTesting
public Bitmap ensureNormalizedIcon(Bitmap icon, String origin) {
if (icon == null || icon.getWidth() == 0) {
if (mIconGenerator == null) {
int cornerRadiusPx = Math.min(mLargeIconWidthPx, mLargeIconHeightPx) / 2;
mIconGenerator =
new RoundedIconGenerator(mLargeIconWidthPx, mLargeIconHeightPx,
cornerRadiusPx,
NOTIFICATION_ICON_BG_COLOR,
NOTIFICATION_TEXT_SIZE_DP * mDensity);
}
return mIconGenerator.generateIconForUrl(origin, true);
}
if (icon.getWidth() > mLargeIconWidthPx || icon.getHeight() > mLargeIconHeightPx) {
return icon.createScaledBitmap(icon, mLargeIconWidthPx, mLargeIconHeightPx,
false /* not filtered */);
}
return icon;
}
/**
* Determines whether to use standard notification layouts, using NotificationCompat.Builder,
* or custom layouts using Chrome's own templates.
*
* The --{enable,disable}-web-notification-custom-layouts
* command line flags take precedence over a running Finch trial.
*
* @return Whether custom layouts should be used.
*/
@VisibleForTesting
static boolean useCustomLayouts() {
// Query the field trial state first to ensure correct UMA reporting.
String groupName = FieldTrialList.findFullName("WebNotificationCustomLayouts");
CommandLine commandLine = CommandLine.getInstance();
if (commandLine.hasSwitch(ChromeSwitches.DISABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS)) {
return false;
}
if (commandLine.hasSwitch(ChromeSwitches.ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS)) {
return true;
}
return !groupName.equals("Disabled");
}
/**
* Returns whether a notification has been clicked in the last 5 seconds.
* Used for Startup.BringToForegroundReason UMA histogram.
*/
public static boolean wasNotificationRecentlyClicked() {
if (sInstance == null) return false;
long now = System.currentTimeMillis();
return now - sInstance.mLastNotificationClickMs < 5 * 1000;
}
/**
* Closes the notification associated with the given parameters.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin to which the notification belongs.
* @param tag The tag of the notification. May be NULL.
*/
@CalledByNative
private void closeNotification(long persistentNotificationId, String origin, String tag) {
String platformTag = makePlatformTag(persistentNotificationId, origin, tag);
mNotificationManager.cancel(platformTag, PLATFORM_ID);
}
/**
* Calls NotificationUIManagerAndroid::OnNotificationClicked in native code to indicate that
* the notification with the given parameters has been clicked on.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin of the notification.
* @param profileId Id of the profile that showed the notification.
* @param incognito if the profile session was an off the record one.
* @param tag The tag of the notification. May be NULL.
*/
private void onNotificationClicked(long persistentNotificationId, String origin,
String profileId, boolean incognito, String tag, int actionIndex) {
mLastNotificationClickMs = System.currentTimeMillis();
nativeOnNotificationClicked(mNativeNotificationManager, persistentNotificationId, origin,
profileId, incognito, tag, actionIndex);
}
/**
* Calls NotificationUIManagerAndroid::OnNotificationClosed in native code to indicate that
* the notification with the given parameters has been closed.
*
* @param persistentNotificationId The persistent id of the notification.
* @param origin The origin of the notification.
* @param profileId Id of the profile that showed the notification.
* @param incognito if the profile session was an off the record one.
* @param tag The tag of the notification. May be NULL.
* @param byUser Whether the notification was closed by a user gesture.
*/
private void onNotificationClosed(long persistentNotificationId, String origin,
String profileId, boolean incognito, String tag, boolean byUser) {
nativeOnNotificationClosed(mNativeNotificationManager, persistentNotificationId, origin,
profileId, incognito, tag, byUser);
}
private static native void nativeInitializeNotificationUIManager();
private native void nativeOnNotificationClicked(long nativeNotificationUIManagerAndroid,
long persistentNotificationId, String origin, String profileId, boolean incognito,
String tag, int actionIndex);
private native void nativeOnNotificationClosed(long nativeNotificationUIManagerAndroid,
long persistentNotificationId, String origin, String profileId, boolean incognito,
String tag, boolean byUser);
}