diff -r 000000000000 -r 6474c204b198 mobile/android/base/NotificationHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/NotificationHandler.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,194 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import org.mozilla.gecko.gfx.BitmapUtils; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +import java.util.concurrent.ConcurrentHashMap; + +public class NotificationHandler { + private final ConcurrentHashMap + mNotifications = new ConcurrentHashMap(); + private final Context mContext; + private final NotificationManager mNotificationManager; + + /** + * Notification associated with this service's foreground state. + * + * {@link android.app.Service#startForeground(int, android.app.Notification)} + * associates the foreground with exactly one notification from the service. + * To keep Fennec alive during downloads (and to make sure it can be killed + * once downloads are complete), we make sure that the foreground is always + * associated with an active progress notification if and only if at least + * one download is in progress. + */ + private Notification mForegroundNotification; + private int mForegroundNotificationId; + + public NotificationHandler(Context context) { + mContext = context; + mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + } + + /** + * Adds a notification. + * + * @param notificationID the unique ID of the notification + * @param aImageUrl URL of the image to use + * @param aAlertTitle title of the notification + * @param aAlertText text of the notification + * @param contentIntent Intent used when the notification is clicked + * @param clearIntent Intent used when the notification is removed + */ + public void add(int notificationID, String aImageUrl, String aAlertTitle, + String aAlertText, PendingIntent contentIntent) { + // Remove the old notification with the same ID, if any + remove(notificationID); + + Uri imageUri = Uri.parse(aImageUrl); + int icon = BitmapUtils.getResource(imageUri, R.drawable.ic_status_logo); + final AlertNotification notification = new AlertNotification(mContext, notificationID, + icon, aAlertTitle, aAlertText, System.currentTimeMillis(), imageUri); + + notification.setLatestEventInfo(mContext, aAlertTitle, aAlertText, contentIntent); + + mNotificationManager.notify(notificationID, notification); + mNotifications.put(notificationID, notification); + } + + /** + * Adds a notification. + * + * @param id the unique ID of the notification + * @param aNotification the Notification to add + */ + public void add(int id, Notification notification) { + mNotificationManager.notify(id, notification); + mNotifications.put(id, notification); + + if (mForegroundNotification == null && isOngoing(notification)) { + setForegroundNotification(id, notification); + } + } + + /** + * Updates a notification. + * + * @param notificationID ID of existing notification + * @param aProgress progress of item being updated + * @param aProgressMax max progress of item being updated + * @param aAlertText text of the notification + */ + public void update(int notificationID, long aProgress, long aProgressMax, String aAlertText) { + final Notification notification = mNotifications.get(notificationID); + if (notification == null) { + return; + } + + if (notification instanceof AlertNotification) { + AlertNotification alert = (AlertNotification)notification; + alert.updateProgress(aAlertText, aProgress, aProgressMax); + } + + if (mForegroundNotification == null && isOngoing(notification)) { + setForegroundNotification(notificationID, notification); + } + } + + /** + * Removes a notification. + * + * @param notificationID ID of existing notification + */ + public void remove(int notificationID) { + final Notification notification = mNotifications.remove(notificationID); + if (notification != null) { + updateForegroundNotification(notificationID, notification); + } + mNotificationManager.cancel(notificationID); + } + + /** + * Determines whether the service is done. + * + * The service is considered finished when all notifications have been + * removed. + * + * @return whether all notifications have been removed + */ + public boolean isDone() { + return mNotifications.isEmpty(); + } + + /** + * Determines whether a notification should hold a foreground service to keep Gecko alive + * + * @param notificationID the id of the notification to check + * @return whether the notification is ongoing + */ + public boolean isOngoing(int notificationID) { + final Notification notification = mNotifications.get(notificationID); + return isOngoing(notification); + } + + /** + * Determines whether a notification should hold a foreground service to keep Gecko alive + * + * @param notification the notification to check + * @return whether the notification is ongoing + */ + public boolean isOngoing(Notification notification) { + if (notification != null && (isProgressStyle(notification) || ((notification.flags & Notification.FLAG_ONGOING_EVENT) > 0))) { + return true; + } + return false; + } + + /** + * Helper method to determines whether a notification is an AlertNotification that is showing progress + * This method will be deprecated when AlertNotifications are removed (bug 893289). + * + * @param notification the notification to check + * @return whether the notification is an AlertNotification showing progress. + */ + private boolean isProgressStyle(Notification notification) { + if (notification != null && notification instanceof AlertNotification) { + return ((AlertNotification)notification).isProgressStyle(); + } + return false; + } + + protected void setForegroundNotification(int id, Notification notification) { + mForegroundNotificationId = id; + mForegroundNotification = notification; + } + + private void updateForegroundNotification(int oldId, Notification oldNotification) { + if (mForegroundNotificationId == oldId) { + // If we're removing the notification associated with the + // foreground, we need to pick another active notification to act + // as the foreground notification. + Notification foregroundNotification = null; + int foregroundId = 0; + for (final Integer id : mNotifications.keySet()) { + final Notification notification = mNotifications.get(id); + if (isOngoing(notification)) { + foregroundNotification = notification; + foregroundId = id; + break; + } + } + setForegroundNotification(foregroundId, foregroundNotification); + } + } +}