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