|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko; |
|
7 |
|
8 import org.mozilla.gecko.gfx.BitmapUtils; |
|
9 |
|
10 import android.app.Notification; |
|
11 import android.app.NotificationManager; |
|
12 import android.app.PendingIntent; |
|
13 import android.content.Context; |
|
14 import android.net.Uri; |
|
15 import android.util.Log; |
|
16 |
|
17 import java.util.concurrent.ConcurrentHashMap; |
|
18 |
|
19 public class NotificationHandler { |
|
20 private final ConcurrentHashMap<Integer, Notification> |
|
21 mNotifications = new ConcurrentHashMap<Integer, Notification>(); |
|
22 private final Context mContext; |
|
23 private final NotificationManager mNotificationManager; |
|
24 |
|
25 /** |
|
26 * Notification associated with this service's foreground state. |
|
27 * |
|
28 * {@link android.app.Service#startForeground(int, android.app.Notification)} |
|
29 * associates the foreground with exactly one notification from the service. |
|
30 * To keep Fennec alive during downloads (and to make sure it can be killed |
|
31 * once downloads are complete), we make sure that the foreground is always |
|
32 * associated with an active progress notification if and only if at least |
|
33 * one download is in progress. |
|
34 */ |
|
35 private Notification mForegroundNotification; |
|
36 private int mForegroundNotificationId; |
|
37 |
|
38 public NotificationHandler(Context context) { |
|
39 mContext = context; |
|
40 mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); |
|
41 } |
|
42 |
|
43 /** |
|
44 * Adds a notification. |
|
45 * |
|
46 * @param notificationID the unique ID of the notification |
|
47 * @param aImageUrl URL of the image to use |
|
48 * @param aAlertTitle title of the notification |
|
49 * @param aAlertText text of the notification |
|
50 * @param contentIntent Intent used when the notification is clicked |
|
51 * @param clearIntent Intent used when the notification is removed |
|
52 */ |
|
53 public void add(int notificationID, String aImageUrl, String aAlertTitle, |
|
54 String aAlertText, PendingIntent contentIntent) { |
|
55 // Remove the old notification with the same ID, if any |
|
56 remove(notificationID); |
|
57 |
|
58 Uri imageUri = Uri.parse(aImageUrl); |
|
59 int icon = BitmapUtils.getResource(imageUri, R.drawable.ic_status_logo); |
|
60 final AlertNotification notification = new AlertNotification(mContext, notificationID, |
|
61 icon, aAlertTitle, aAlertText, System.currentTimeMillis(), imageUri); |
|
62 |
|
63 notification.setLatestEventInfo(mContext, aAlertTitle, aAlertText, contentIntent); |
|
64 |
|
65 mNotificationManager.notify(notificationID, notification); |
|
66 mNotifications.put(notificationID, notification); |
|
67 } |
|
68 |
|
69 /** |
|
70 * Adds a notification. |
|
71 * |
|
72 * @param id the unique ID of the notification |
|
73 * @param aNotification the Notification to add |
|
74 */ |
|
75 public void add(int id, Notification notification) { |
|
76 mNotificationManager.notify(id, notification); |
|
77 mNotifications.put(id, notification); |
|
78 |
|
79 if (mForegroundNotification == null && isOngoing(notification)) { |
|
80 setForegroundNotification(id, notification); |
|
81 } |
|
82 } |
|
83 |
|
84 /** |
|
85 * Updates a notification. |
|
86 * |
|
87 * @param notificationID ID of existing notification |
|
88 * @param aProgress progress of item being updated |
|
89 * @param aProgressMax max progress of item being updated |
|
90 * @param aAlertText text of the notification |
|
91 */ |
|
92 public void update(int notificationID, long aProgress, long aProgressMax, String aAlertText) { |
|
93 final Notification notification = mNotifications.get(notificationID); |
|
94 if (notification == null) { |
|
95 return; |
|
96 } |
|
97 |
|
98 if (notification instanceof AlertNotification) { |
|
99 AlertNotification alert = (AlertNotification)notification; |
|
100 alert.updateProgress(aAlertText, aProgress, aProgressMax); |
|
101 } |
|
102 |
|
103 if (mForegroundNotification == null && isOngoing(notification)) { |
|
104 setForegroundNotification(notificationID, notification); |
|
105 } |
|
106 } |
|
107 |
|
108 /** |
|
109 * Removes a notification. |
|
110 * |
|
111 * @param notificationID ID of existing notification |
|
112 */ |
|
113 public void remove(int notificationID) { |
|
114 final Notification notification = mNotifications.remove(notificationID); |
|
115 if (notification != null) { |
|
116 updateForegroundNotification(notificationID, notification); |
|
117 } |
|
118 mNotificationManager.cancel(notificationID); |
|
119 } |
|
120 |
|
121 /** |
|
122 * Determines whether the service is done. |
|
123 * |
|
124 * The service is considered finished when all notifications have been |
|
125 * removed. |
|
126 * |
|
127 * @return whether all notifications have been removed |
|
128 */ |
|
129 public boolean isDone() { |
|
130 return mNotifications.isEmpty(); |
|
131 } |
|
132 |
|
133 /** |
|
134 * Determines whether a notification should hold a foreground service to keep Gecko alive |
|
135 * |
|
136 * @param notificationID the id of the notification to check |
|
137 * @return whether the notification is ongoing |
|
138 */ |
|
139 public boolean isOngoing(int notificationID) { |
|
140 final Notification notification = mNotifications.get(notificationID); |
|
141 return isOngoing(notification); |
|
142 } |
|
143 |
|
144 /** |
|
145 * Determines whether a notification should hold a foreground service to keep Gecko alive |
|
146 * |
|
147 * @param notification the notification to check |
|
148 * @return whether the notification is ongoing |
|
149 */ |
|
150 public boolean isOngoing(Notification notification) { |
|
151 if (notification != null && (isProgressStyle(notification) || ((notification.flags & Notification.FLAG_ONGOING_EVENT) > 0))) { |
|
152 return true; |
|
153 } |
|
154 return false; |
|
155 } |
|
156 |
|
157 /** |
|
158 * Helper method to determines whether a notification is an AlertNotification that is showing progress |
|
159 * This method will be deprecated when AlertNotifications are removed (bug 893289). |
|
160 * |
|
161 * @param notification the notification to check |
|
162 * @return whether the notification is an AlertNotification showing progress. |
|
163 */ |
|
164 private boolean isProgressStyle(Notification notification) { |
|
165 if (notification != null && notification instanceof AlertNotification) { |
|
166 return ((AlertNotification)notification).isProgressStyle(); |
|
167 } |
|
168 return false; |
|
169 } |
|
170 |
|
171 protected void setForegroundNotification(int id, Notification notification) { |
|
172 mForegroundNotificationId = id; |
|
173 mForegroundNotification = notification; |
|
174 } |
|
175 |
|
176 private void updateForegroundNotification(int oldId, Notification oldNotification) { |
|
177 if (mForegroundNotificationId == oldId) { |
|
178 // If we're removing the notification associated with the |
|
179 // foreground, we need to pick another active notification to act |
|
180 // as the foreground notification. |
|
181 Notification foregroundNotification = null; |
|
182 int foregroundId = 0; |
|
183 for (final Integer id : mNotifications.keySet()) { |
|
184 final Notification notification = mNotifications.get(id); |
|
185 if (isOngoing(notification)) { |
|
186 foregroundNotification = notification; |
|
187 foregroundId = id; |
|
188 break; |
|
189 } |
|
190 } |
|
191 setForegroundNotification(foregroundId, foregroundNotification); |
|
192 } |
|
193 } |
|
194 } |