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