1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/NotificationHandler.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,194 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import org.mozilla.gecko.gfx.BitmapUtils; 1.12 + 1.13 +import android.app.Notification; 1.14 +import android.app.NotificationManager; 1.15 +import android.app.PendingIntent; 1.16 +import android.content.Context; 1.17 +import android.net.Uri; 1.18 +import android.util.Log; 1.19 + 1.20 +import java.util.concurrent.ConcurrentHashMap; 1.21 + 1.22 +public class NotificationHandler { 1.23 + private final ConcurrentHashMap<Integer, Notification> 1.24 + mNotifications = new ConcurrentHashMap<Integer, Notification>(); 1.25 + private final Context mContext; 1.26 + private final NotificationManager mNotificationManager; 1.27 + 1.28 + /** 1.29 + * Notification associated with this service's foreground state. 1.30 + * 1.31 + * {@link android.app.Service#startForeground(int, android.app.Notification)} 1.32 + * associates the foreground with exactly one notification from the service. 1.33 + * To keep Fennec alive during downloads (and to make sure it can be killed 1.34 + * once downloads are complete), we make sure that the foreground is always 1.35 + * associated with an active progress notification if and only if at least 1.36 + * one download is in progress. 1.37 + */ 1.38 + private Notification mForegroundNotification; 1.39 + private int mForegroundNotificationId; 1.40 + 1.41 + public NotificationHandler(Context context) { 1.42 + mContext = context; 1.43 + mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 1.44 + } 1.45 + 1.46 + /** 1.47 + * Adds a notification. 1.48 + * 1.49 + * @param notificationID the unique ID of the notification 1.50 + * @param aImageUrl URL of the image to use 1.51 + * @param aAlertTitle title of the notification 1.52 + * @param aAlertText text of the notification 1.53 + * @param contentIntent Intent used when the notification is clicked 1.54 + * @param clearIntent Intent used when the notification is removed 1.55 + */ 1.56 + public void add(int notificationID, String aImageUrl, String aAlertTitle, 1.57 + String aAlertText, PendingIntent contentIntent) { 1.58 + // Remove the old notification with the same ID, if any 1.59 + remove(notificationID); 1.60 + 1.61 + Uri imageUri = Uri.parse(aImageUrl); 1.62 + int icon = BitmapUtils.getResource(imageUri, R.drawable.ic_status_logo); 1.63 + final AlertNotification notification = new AlertNotification(mContext, notificationID, 1.64 + icon, aAlertTitle, aAlertText, System.currentTimeMillis(), imageUri); 1.65 + 1.66 + notification.setLatestEventInfo(mContext, aAlertTitle, aAlertText, contentIntent); 1.67 + 1.68 + mNotificationManager.notify(notificationID, notification); 1.69 + mNotifications.put(notificationID, notification); 1.70 + } 1.71 + 1.72 + /** 1.73 + * Adds a notification. 1.74 + * 1.75 + * @param id the unique ID of the notification 1.76 + * @param aNotification the Notification to add 1.77 + */ 1.78 + public void add(int id, Notification notification) { 1.79 + mNotificationManager.notify(id, notification); 1.80 + mNotifications.put(id, notification); 1.81 + 1.82 + if (mForegroundNotification == null && isOngoing(notification)) { 1.83 + setForegroundNotification(id, notification); 1.84 + } 1.85 + } 1.86 + 1.87 + /** 1.88 + * Updates a notification. 1.89 + * 1.90 + * @param notificationID ID of existing notification 1.91 + * @param aProgress progress of item being updated 1.92 + * @param aProgressMax max progress of item being updated 1.93 + * @param aAlertText text of the notification 1.94 + */ 1.95 + public void update(int notificationID, long aProgress, long aProgressMax, String aAlertText) { 1.96 + final Notification notification = mNotifications.get(notificationID); 1.97 + if (notification == null) { 1.98 + return; 1.99 + } 1.100 + 1.101 + if (notification instanceof AlertNotification) { 1.102 + AlertNotification alert = (AlertNotification)notification; 1.103 + alert.updateProgress(aAlertText, aProgress, aProgressMax); 1.104 + } 1.105 + 1.106 + if (mForegroundNotification == null && isOngoing(notification)) { 1.107 + setForegroundNotification(notificationID, notification); 1.108 + } 1.109 + } 1.110 + 1.111 + /** 1.112 + * Removes a notification. 1.113 + * 1.114 + * @param notificationID ID of existing notification 1.115 + */ 1.116 + public void remove(int notificationID) { 1.117 + final Notification notification = mNotifications.remove(notificationID); 1.118 + if (notification != null) { 1.119 + updateForegroundNotification(notificationID, notification); 1.120 + } 1.121 + mNotificationManager.cancel(notificationID); 1.122 + } 1.123 + 1.124 + /** 1.125 + * Determines whether the service is done. 1.126 + * 1.127 + * The service is considered finished when all notifications have been 1.128 + * removed. 1.129 + * 1.130 + * @return whether all notifications have been removed 1.131 + */ 1.132 + public boolean isDone() { 1.133 + return mNotifications.isEmpty(); 1.134 + } 1.135 + 1.136 + /** 1.137 + * Determines whether a notification should hold a foreground service to keep Gecko alive 1.138 + * 1.139 + * @param notificationID the id of the notification to check 1.140 + * @return whether the notification is ongoing 1.141 + */ 1.142 + public boolean isOngoing(int notificationID) { 1.143 + final Notification notification = mNotifications.get(notificationID); 1.144 + return isOngoing(notification); 1.145 + } 1.146 + 1.147 + /** 1.148 + * Determines whether a notification should hold a foreground service to keep Gecko alive 1.149 + * 1.150 + * @param notification the notification to check 1.151 + * @return whether the notification is ongoing 1.152 + */ 1.153 + public boolean isOngoing(Notification notification) { 1.154 + if (notification != null && (isProgressStyle(notification) || ((notification.flags & Notification.FLAG_ONGOING_EVENT) > 0))) { 1.155 + return true; 1.156 + } 1.157 + return false; 1.158 + } 1.159 + 1.160 + /** 1.161 + * Helper method to determines whether a notification is an AlertNotification that is showing progress 1.162 + * This method will be deprecated when AlertNotifications are removed (bug 893289). 1.163 + * 1.164 + * @param notification the notification to check 1.165 + * @return whether the notification is an AlertNotification showing progress. 1.166 + */ 1.167 + private boolean isProgressStyle(Notification notification) { 1.168 + if (notification != null && notification instanceof AlertNotification) { 1.169 + return ((AlertNotification)notification).isProgressStyle(); 1.170 + } 1.171 + return false; 1.172 + } 1.173 + 1.174 + protected void setForegroundNotification(int id, Notification notification) { 1.175 + mForegroundNotificationId = id; 1.176 + mForegroundNotification = notification; 1.177 + } 1.178 + 1.179 + private void updateForegroundNotification(int oldId, Notification oldNotification) { 1.180 + if (mForegroundNotificationId == oldId) { 1.181 + // If we're removing the notification associated with the 1.182 + // foreground, we need to pick another active notification to act 1.183 + // as the foreground notification. 1.184 + Notification foregroundNotification = null; 1.185 + int foregroundId = 0; 1.186 + for (final Integer id : mNotifications.keySet()) { 1.187 + final Notification notification = mNotifications.get(id); 1.188 + if (isOngoing(notification)) { 1.189 + foregroundNotification = notification; 1.190 + foregroundId = id; 1.191 + break; 1.192 + } 1.193 + } 1.194 + setForegroundNotification(foregroundId, foregroundNotification); 1.195 + } 1.196 + } 1.197 +}