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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java

Issue 2730243002: Update the download summary icon (Closed)
Patch Set: Addressed nit Created 3 years, 9 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 | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java » ('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/download/DownloadNotificationService.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 979b2ba628be529f56c999ee86db0770b2dea8c2..d3da69c8270e8c4783452f99501b4bf034ae8d3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -23,9 +23,11 @@ import android.graphics.Rect;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.Pair;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.ApplicationStatus;
@@ -59,7 +61,7 @@ import java.util.concurrent.TimeUnit;
*
* On O and above, this service will receive {@link Service#startForeground(int, Notification)}
* calls when containing active downloads. The foreground notification will be the summary
- * notification generated by {@link DownloadNotificationService#getSummaryNotification(Context)}.
+ * notification generated by {@link DownloadNotificationService#buildSummaryNotification(Context)}.
* The service will receive a {@link Service#stopForeground(boolean)} call when all active downloads
* are paused. The summary notification will be hidden when there are no other notifications in the
* {@link NotificationConstants#GROUP_DOWNLOADS} group. This gets checked after every notification
@@ -93,6 +95,8 @@ public class DownloadNotificationService extends Service {
/** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */
+ private static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID =
+ "Chrome.NotificationBundleIconIdExtra";
private static final int STARTING_NOTIFICATION_ID = 1000000;
private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5;
@VisibleForTesting static final long SECONDS_PER_MINUTE = TimeUnit.MINUTES.toSeconds(1);
@@ -145,7 +149,7 @@ public class DownloadNotificationService extends Service {
intent.setComponent(new ComponentName(context, DownloadNotificationService.class));
if (BuildInfo.isAtLeastO()) {
- Notification notification = getSummaryNotification(context);
+ Notification notification = buildSummaryNotification(context);
AppHooks.get().startServiceWithNotification(
intent, NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, notification);
@@ -155,16 +159,101 @@ public class DownloadNotificationService extends Service {
}
/**
- * Builds a summary notification that represents downloads. This is the notification passed to
- * {@link #startForeground(int, Notification)}, which keeps this service in the foreground.
- * @param context The context used to build the notification and pull specific resources.
- * @return The {@link Notification} to show for the summary. Meant to be used by
- * {@link NotificationManager#notify(int, Notification)}.
+ * Calculates the suggested icon for the summary notification based on the other notifications
+ * currently showing.
+ * @param context A context to use to query Android-specific information (NotificationManager).
+ * @param removedNotificationId The id of a notification that is currently closing and should be
+ * ignored. -1 if no notifications are being closed.
+ * @param addedNotification A {@link Pair} of <id, Notification> of a notification that is
+ * currently being added and should be used in addition to or in
+ * place of the existing icons.
+ * @return A {@link Pair} that represents both whether or not the new icon
+ * is different from the old one and the icon id itself.
+ */
+ @TargetApi(Build.VERSION_CODES.M)
+ private static Pair<Boolean, Integer> getSummaryIcon(Context context, int removedNotificationId,
+ Pair<Integer, Notification> addedNotification) {
+ if (!BuildInfo.isAtLeastO()) return new Pair<Boolean, Integer>(false, -1);
+ boolean progress = false;
+ boolean paused = false;
+ boolean pending = false;
+ boolean completed = false;
+ boolean failed = false;
+
+ final int progressIcon = android.R.drawable.stat_sys_download;
+ final int pausedIcon = R.drawable.ic_download_pause;
+ final int pendingIcon = R.drawable.ic_download_pending;
+ final int completedIcon = R.drawable.offline_pin;
+ final int failedIcon = android.R.drawable.stat_sys_download_done;
+
+ NotificationManager manager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ StatusBarNotification[] notifications = manager.getActiveNotifications();
+
+ int oldIcon = -1;
+ for (StatusBarNotification notification : notifications) {
+ boolean isDownloadsGroup = TextUtils.equals(notification.getNotification().getGroup(),
+ NotificationConstants.GROUP_DOWNLOADS);
+ if (!isDownloadsGroup) continue;
+ if (notification.getId() == removedNotificationId) continue;
+
+ boolean isSummaryNotification =
+ notification.getId() == NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY;
+
+ if (addedNotification != null && addedNotification.first == notification.getId())
+ continue;
+
+ int icon =
+ notification.getNotification().extras.getInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID);
+ if (isSummaryNotification) {
+ oldIcon = icon;
+ continue;
+ }
+
+ progress |= icon == progressIcon;
+ paused |= icon == pausedIcon;
+ pending |= icon == pendingIcon;
+ completed |= icon == completedIcon;
+ failed |= icon == failedIcon;
+ }
+
+ if (addedNotification != null) {
+ int icon = addedNotification.second.extras.getInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID);
+
+ progress |= icon == progressIcon;
+ paused |= icon == pausedIcon;
+ pending |= icon == pendingIcon;
+ completed |= icon == completedIcon;
+ failed |= icon == failedIcon;
+ }
+
+ int newIcon = android.R.drawable.stat_sys_download_done;
+ if (progress) {
+ newIcon = android.R.drawable.stat_sys_download;
+ } else if (paused) {
+ newIcon = R.drawable.ic_download_pause;
+ } else if (pending) {
+ newIcon = R.drawable.ic_download_pending;
+ } else if (completed) {
+ newIcon = R.drawable.offline_pin;
+ } else if (failed) {
+ newIcon = android.R.drawable.stat_sys_download_done;
+ }
+ return new Pair<Boolean, Integer>(newIcon != oldIcon, newIcon);
+ }
+
+ /**
+ * Builds a summary notification that represents all downloads.
+ * {@see #buildSummaryNotification(Context)}.
+ * @param context A context used to query Android strings and resources.
+ * @param iconId The id of an icon to use for the notification.
+ * @return a {@link Notification} that represents the summary icon for all downloads.
*/
- private static Notification getSummaryNotification(Context context) {
+ private static Notification buildSummaryNotificationWithIcon(Context context, int iconId) {
String title =
context.getResources().getString(R.string.download_notification_summary_title,
DownloadSharedPreferenceHelper.getInstance().getEntries().size());
+
ChromeNotificationBuilder builder =
AppHooks.get()
.createChromeNotificationBuilder(true /* preferCompat */,
@@ -173,10 +262,13 @@ public class DownloadNotificationService extends Service {
NotificationConstants.CATEGORY_GROUP_ID_GENERAL,
context.getString(R.string.notification_category_group_general))
.setContentTitle(title)
- .setSmallIcon(android.R.drawable.stat_sys_download_done)
+ .setSmallIcon(iconId)
.setLocalOnly(true)
.setGroup(NotificationConstants.GROUP_DOWNLOADS)
.setGroupSummary(true);
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId);
+ builder.addExtras(extras);
// This notification should not actually be shown. But if it is, set the click intent to
// open downloads home.
@@ -190,6 +282,18 @@ public class DownloadNotificationService extends Service {
}
/**
+ * Builds a summary notification that represents downloads. This is the notification passed to
+ * {@link #startForeground(int, Notification)}, which keeps this service in the foreground.
+ * @param context The context used to build the notification and pull specific resources.
+ * @return The {@link Notification} to show for the summary. Meant to be used by
+ * {@link NotificationManager#notify(int, Notification)}.
+ */
+ private static Notification buildSummaryNotification(Context context) {
+ Pair<Boolean, Integer> icon = getSummaryIcon(context, -1, null);
+ return buildSummaryNotificationWithIcon(context, icon.second);
+ }
+
+ /**
* @return Whether or not there are any current resumable downloads being tracked. These
* tracked downloads may not currently be showing notifications.
*/
@@ -305,12 +409,12 @@ public class DownloadNotificationService extends Service {
/**
* On >= O Android releases, puts this service into a foreground state, binding it to the
- * {@link Notification} generated by {@link #getSummaryNotification(Context)}.
+ * {@link Notification} generated by {@link #buildSummaryNotification(Context)}.
*/
@VisibleForTesting
void startForegroundInternal() {
if (!BuildInfo.isAtLeastO()) return;
- Notification notification = getSummaryNotification(getApplicationContext());
+ Notification notification = buildSummaryNotification(getApplicationContext());
startForeground(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, notification);
}
@@ -441,6 +545,30 @@ public class DownloadNotificationService extends Service {
}
/**
+ * Updates the notification summary with a new icon, if necessary.
+ * @param removedNotificationId The id of a notification that is currently closing and should be
+ * ignored. -1 if no notifications are being closed.
+ * @param addedNotification A {@link Pair} of <id, Notification> of a notification that is
+ * currently being added and should be used in addition to or in
+ * place of the existing icons.
+ */
+ @VisibleForTesting
+ void updateSummaryIcon(
+ int removedNotificationId, Pair<Integer, Notification> addedNotification) {
+ if (!BuildInfo.isAtLeastO()) return;
+
+ Pair<Boolean, Integer> icon =
+ getSummaryIcon(mContext, removedNotificationId, addedNotification);
+
+ // Avoid rebuilding the summary notification if the icon hasn't changed or we have (or are
+ // about to have) no active downloads.
+ if (!icon.first || !hasDownloadNotifications(removedNotificationId)) return;
+
+ mNotificationManager.notify(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY,
+ buildSummaryNotificationWithIcon(mContext, icon.second));
+ }
+
+ /**
* Cancels the existing summary notification. Moved to a helper method for test mocking.
*/
@VisibleForTesting
@@ -460,11 +588,11 @@ public class DownloadNotificationService extends Service {
* Or pass {@code null} if no notification fits that criteria.
*/
@TargetApi(Build.VERSION_CODES.M)
- void hideSummaryNotificationIfNecessary(Integer notificationIdToIgnore) {
- if (!BuildInfo.isAtLeastO()) return;
- if (mDownloadsInProgress.size() > 0) return;
+ boolean hideSummaryNotificationIfNecessary(Integer notificationIdToIgnore) {
+ if (!BuildInfo.isAtLeastO()) return false;
+ if (mDownloadsInProgress.size() > 0) return false;
- if (hasDownloadNotifications(notificationIdToIgnore)) return;
+ if (hasDownloadNotifications(notificationIdToIgnore)) return false;
StatusBarNotification notification = getSummaryNotification();
if (notification != null) {
@@ -495,6 +623,7 @@ public class DownloadNotificationService extends Service {
// Stop the service which should start the destruction process. At this point we should be
// (1) a background service and (2) unbound from any clients.
stopSelf();
+ return true;
}
@Override
@@ -654,7 +783,9 @@ public class DownloadNotificationService extends Service {
// down, don't stop foreground if we have no download notifications left to show. Hiding
// the summary will take care of that for us.
stopTrackingInProgressDownload(downloadGuid, hasDownloadNotifications(notificationId));
- hideSummaryNotificationIfNecessary(notificationId);
+ if (!hideSummaryNotificationIfNecessary(notificationId)) {
+ updateSummaryIcon(notificationId, null);
+ }
}
/**
@@ -834,6 +965,9 @@ public class DownloadNotificationService extends Service {
*/
private ChromeNotificationBuilder buildNotification(
int iconId, String title, String contentText) {
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId);
+
ChromeNotificationBuilder builder =
AppHooks.get()
.createChromeNotificationBuilder(true /* preferCompat */,
@@ -847,7 +981,8 @@ public class DownloadNotificationService extends Service {
.setLocalOnly(true)
.setAutoCancel(true)
.setContentText(contentText)
- .setGroup(NotificationConstants.GROUP_DOWNLOADS);
+ .setGroup(NotificationConstants.GROUP_DOWNLOADS)
+ .addExtras(extras);
return builder;
}
@@ -1061,6 +1196,7 @@ public class DownloadNotificationService extends Service {
} else {
mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid);
}
+ updateSummaryIcon(-1, new Pair<Integer, Notification>(id, notification));
}
private void trackNotificationUma(boolean isOfflinePage, String downloadGuid) {
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698