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