1package eu.siacs.conversations.services;
2
3import java.util.ArrayList;
4import java.util.LinkedHashMap;
5import java.util.regex.Matcher;
6import java.util.regex.Pattern;
7
8import android.app.Notification;
9import android.app.NotificationManager;
10import android.app.PendingIntent;
11import android.content.Context;
12import android.content.Intent;
13import android.content.SharedPreferences;
14import android.net.Uri;
15import android.support.v4.app.NotificationCompat;
16import android.support.v4.app.TaskStackBuilder;
17import android.text.Html;
18
19import eu.siacs.conversations.R;
20import eu.siacs.conversations.entities.Conversation;
21import eu.siacs.conversations.entities.Message;
22import eu.siacs.conversations.ui.ConversationActivity;
23
24public class NotificationService {
25
26 private XmppConnectionService mXmppConnectionService;
27 private NotificationManager mNotificationManager;
28
29 private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
30
31 public int NOTIFICATION_ID = 0x2342;
32 private Conversation mOpenConversation;
33 private boolean mIsInForeground;
34
35 public NotificationService(XmppConnectionService service) {
36 this.mXmppConnectionService = service;
37 this.mNotificationManager = (NotificationManager) service
38 .getSystemService(Context.NOTIFICATION_SERVICE);
39 }
40
41 public synchronized void push(Message message) {
42 if (this.mIsInForeground
43 && this.mOpenConversation == message.getConversation()) {
44 return; // simply ignore
45 }
46 String conversationUuid = message.getConversationUuid();
47 if (notifications.containsKey(conversationUuid)) {
48 notifications.get(conversationUuid).add(message);
49 } else {
50 ArrayList<Message> mList = new ArrayList<Message>();
51 mList.add(message);
52 notifications.put(conversationUuid, mList);
53 }
54 updateNotification(!(this.mIsInForeground && this.mOpenConversation == null));
55 }
56
57 public void clear() {
58 notifications.clear();
59 updateNotification(false);
60 }
61
62 public void clear(Conversation conversation) {
63 notifications.remove(conversation.getUuid());
64 updateNotification(false);
65 }
66
67 private void updateNotification(boolean notify) {
68 SharedPreferences preferences = mXmppConnectionService.getPreferences();
69
70 String ringtone = preferences.getString("notification_ringtone", null);
71 boolean vibrate = preferences.getBoolean("vibrate_on_notification",
72 true);
73
74 if (notifications.size() == 0) {
75 mNotificationManager.cancel(NOTIFICATION_ID);
76 } else {
77 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
78 mXmppConnectionService);
79 mBuilder.setSmallIcon(R.drawable.ic_notification);
80 if (notifications.size() == 1) {
81 ArrayList<Message> messages = notifications.values().iterator()
82 .next();
83 if (messages.size() >= 1) {
84 Conversation conversation = messages.get(0)
85 .getConversation();
86 mBuilder.setLargeIcon(conversation.getImage(
87 mXmppConnectionService, 64));
88 mBuilder.setContentTitle(conversation.getName());
89 StringBuilder text = new StringBuilder();
90 for (int i = 0; i < messages.size(); ++i) {
91 text.append(messages.get(i).getReadableBody(
92 mXmppConnectionService));
93 if (i != messages.size() - 1) {
94 text.append("\n");
95 }
96 }
97 mBuilder.setStyle(new NotificationCompat.BigTextStyle()
98 .bigText(text.toString()));
99 mBuilder.setContentText(messages.get(0).getReadableBody(
100 mXmppConnectionService));
101 if (notify) {
102 mBuilder.setTicker(messages.get(messages.size() - 1)
103 .getReadableBody(mXmppConnectionService));
104 }
105 mBuilder.setContentIntent(createContentIntent(conversation
106 .getUuid()));
107 } else {
108 mNotificationManager.cancel(NOTIFICATION_ID);
109 return;
110 }
111 } else {
112 NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
113 style.setBigContentTitle(notifications.size()
114 + " "
115 + mXmppConnectionService
116 .getString(R.string.unread_conversations));
117 StringBuilder names = new StringBuilder();
118 Conversation conversation = null;
119 for (ArrayList<Message> messages : notifications.values()) {
120 if (messages.size() > 0) {
121 conversation = messages.get(0).getConversation();
122 String name = conversation.getName();
123 style.addLine(Html.fromHtml("<b>"
124 + name
125 + "</b> "
126 + messages.get(0).getReadableBody(
127 mXmppConnectionService)));
128 names.append(name);
129 names.append(", ");
130 }
131 }
132 if (names.length() >= 2) {
133 names.delete(names.length() - 2, names.length());
134 }
135 mBuilder.setContentTitle(notifications.size()
136 + " "
137 + mXmppConnectionService
138 .getString(R.string.unread_conversations));
139 mBuilder.setContentText(names.toString());
140 mBuilder.setStyle(style);
141 if (conversation != null) {
142 mBuilder.setContentIntent(createContentIntent(conversation
143 .getUuid()));
144 }
145 }
146 if (notify) {
147 if (vibrate) {
148 int dat = 70;
149 long[] pattern = { 0, 3 * dat, dat, dat };
150 mBuilder.setVibrate(pattern);
151 }
152 if (ringtone != null) {
153 mBuilder.setSound(Uri.parse(ringtone));
154 }
155 }
156 mBuilder.setDeleteIntent(createDeleteIntent());
157 mBuilder.setLights(0xffffffff, 2000, 4000);
158 Notification notification = mBuilder.build();
159 mNotificationManager.notify(NOTIFICATION_ID, notification);
160 }
161 }
162
163 private PendingIntent createContentIntent(String conversationUuid) {
164 TaskStackBuilder stackBuilder = TaskStackBuilder
165 .create(mXmppConnectionService);
166 stackBuilder.addParentStack(ConversationActivity.class);
167
168 Intent viewConversationIntent = new Intent(mXmppConnectionService,
169 ConversationActivity.class);
170 viewConversationIntent.setAction(Intent.ACTION_VIEW);
171 viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
172 conversationUuid);
173 viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
174
175 stackBuilder.addNextIntent(viewConversationIntent);
176
177 PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
178 PendingIntent.FLAG_UPDATE_CURRENT);
179 return resultPendingIntent;
180 }
181
182 private PendingIntent createDeleteIntent() {
183 Intent intent = new Intent(mXmppConnectionService,
184 XmppConnectionService.class);
185 intent.setAction("clear_notification");
186 return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
187 }
188
189 public static boolean wasHighlightedOrPrivate(Message message) {
190 String nick = message.getConversation().getMucOptions().getActualNick();
191 Pattern highlight = generateNickHighlightPattern(nick);
192 Matcher m = highlight.matcher(message.getBody());
193 return (m.find() || message.getType() == Message.TYPE_PRIVATE);
194 }
195
196 private static Pattern generateNickHighlightPattern(String nick) {
197 // We expect a word boundary, i.e. space or start of string, followed by
198 // the
199 // nick (matched in case-insensitive manner), followed by optional
200 // punctuation (for example "bob: i disagree" or "how are you alice?"),
201 // followed by another word boundary.
202 return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b",
203 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
204 }
205
206 public void setOpenConversation(Conversation conversation) {
207 this.mOpenConversation = conversation;
208 }
209
210 public void setIsInForeground(boolean foreground) {
211 this.mIsInForeground = foreground;
212 }
213
214}