blob: 227f8f4259a9849e3fec5fcc8054d988024a8bb3 [file] [log] [blame]
// Copyright 2017 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.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.SharedPreferences;
import android.os.Build;
import android.support.annotation.IntDef;
import android.support.v4.app.NotificationManagerCompat;
import org.chromium.base.ContextUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.CachedMetrics;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Helper class to make tracking notification UMA stats easier for various features. Having a
* single entry point here to make more complex tracking easier to add in the future.
*/
public class NotificationUmaTracker {
private static final String TAG = "NotifsUMATracker";
/*
* A list of notification types. To add a type to this list please update
* SystemNotificationType in enums.xml and make sure to keep this list in sync. Additions
* should be treated as APPEND ONLY to keep the UMA metric semantics the same over time.
*
* A SystemNotificationType value can also be saved in shared preferences.
*/
@IntDef({SystemNotificationType.UNKNOWN, SystemNotificationType.DOWNLOAD_FILES,
SystemNotificationType.DOWNLOAD_PAGES, SystemNotificationType.CLOSE_INCOGNITO,
SystemNotificationType.CONTENT_SUGGESTION, SystemNotificationType.MEDIA_CAPTURE,
SystemNotificationType.PHYSICAL_WEB, SystemNotificationType.MEDIA,
SystemNotificationType.SITES, SystemNotificationType.SYNC,
SystemNotificationType.WEBAPK, SystemNotificationType.BROWSER_ACTIONS,
SystemNotificationType.WEBAPP_ACTIONS,
SystemNotificationType.OFFLINE_CONTENT_SUGGESTION,
SystemNotificationType.TRUSTED_WEB_ACTIVITY_SITES})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemNotificationType {
int UNKNOWN = -1;
int DOWNLOAD_FILES = 0;
int DOWNLOAD_PAGES = 1;
int CLOSE_INCOGNITO = 2;
int CONTENT_SUGGESTION = 3;
int MEDIA_CAPTURE = 4;
int PHYSICAL_WEB = 5;
int MEDIA = 6;
int SITES = 7;
int SYNC = 8;
int WEBAPK = 9;
int BROWSER_ACTIONS = 10;
int WEBAPP_ACTIONS = 11;
int OFFLINE_CONTENT_SUGGESTION = 12;
int TRUSTED_WEB_ACTIVITY_SITES = 13;
int NUM_ENTRIES = 14;
}
private static final String LAST_SHOWN_NOTIFICATION_TYPE_KEY =
"NotificationUmaTracker.LastShownNotificationType";
private static class LazyHolder {
private static final NotificationUmaTracker INSTANCE = new NotificationUmaTracker();
}
/** Cached objects. */
private final SharedPreferences mSharedPreferences;
private final NotificationManagerCompat mNotificationManager;
public static NotificationUmaTracker getInstance() {
return LazyHolder.INSTANCE;
}
private NotificationUmaTracker() {
mSharedPreferences = ContextUtils.getAppSharedPreferences();
mNotificationManager = NotificationManagerCompat.from(ContextUtils.getApplicationContext());
}
/**
* Logs {@link android.app.Notification} usage, categorized into {@link SystemNotificationType}
* types. Splits the logs by the global enabled state of notifications and also logs the last
* notification shown prior to the global notifications state being disabled by the user.
* @param type The type of notification that was shown.
* @param notification The notification that was shown.
* @see SystemNotificationType
*/
public void onNotificationShown(@SystemNotificationType int type, Notification notification) {
if (type == SystemNotificationType.UNKNOWN) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
logNotificationShown(type, notification.getChannelId());
} else {
logNotificationShown(type, null);
}
}
/**
* Logs notification click event when the user taps on the notification body.
* @param type Type of the notification.
*/
public void onNotificationContentClick(@SystemNotificationType int type) {
if (type == SystemNotificationType.UNKNOWN) return;
new CachedMetrics
.EnumeratedHistogramSample("Mobile.SystemNotification.Content.Click",
SystemNotificationType.NUM_ENTRIES)
.record(type);
}
private void logNotificationShown(
@SystemNotificationType int type, @ChannelDefinitions.ChannelId String channelId) {
if (!mNotificationManager.areNotificationsEnabled()) {
logPotentialBlockedCause();
recordHistogram("Mobile.SystemNotification.Blocked", type);
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && channelId != null
&& isChannelBlocked(channelId)) {
recordHistogram("Mobile.SystemNotification.ChannelBlocked", type);
return;
}
saveLastShownNotification(type);
recordHistogram("Mobile.SystemNotification.Shown", type);
}
@TargetApi(26)
private boolean isChannelBlocked(@ChannelDefinitions.ChannelId String channelId) {
// Use non-compat notification manager as compat does not have getNotificationChannel (yet).
NotificationManager notificationManager =
ContextUtils.getApplicationContext().getSystemService(NotificationManager.class);
NotificationChannel channel = notificationManager.getNotificationChannel(channelId);
return channel != null && channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
}
private void saveLastShownNotification(@SystemNotificationType int type) {
mSharedPreferences.edit().putInt(LAST_SHOWN_NOTIFICATION_TYPE_KEY, type).apply();
}
private void logPotentialBlockedCause() {
int lastType = mSharedPreferences.getInt(
LAST_SHOWN_NOTIFICATION_TYPE_KEY, SystemNotificationType.UNKNOWN);
if (lastType == -1) return;
mSharedPreferences.edit().remove(LAST_SHOWN_NOTIFICATION_TYPE_KEY).apply();
recordHistogram("Mobile.SystemNotification.BlockedAfterShown", lastType);
}
private static void recordHistogram(String name, @SystemNotificationType int type) {
if (type == SystemNotificationType.UNKNOWN) return;
if (!LibraryLoader.getInstance().isInitialized()) return;
RecordHistogram.recordEnumeratedHistogram(name, type, SystemNotificationType.NUM_ENTRIES);
}
}