aboutsummaryrefslogtreecommitdiffstats
path: root/src/eu/siacs/conversations/services
diff options
context:
space:
mode:
Diffstat (limited to 'src/eu/siacs/conversations/services')
-rw-r--r--src/eu/siacs/conversations/services/EventReceiver.java3
-rw-r--r--src/eu/siacs/conversations/services/ImageProvider.java2
-rw-r--r--src/eu/siacs/conversations/services/NotificationService.java241
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java154
4 files changed, 346 insertions, 54 deletions
diff --git a/src/eu/siacs/conversations/services/EventReceiver.java b/src/eu/siacs/conversations/services/EventReceiver.java
index e2445b2a..dfbe9db7 100644
--- a/src/eu/siacs/conversations/services/EventReceiver.java
+++ b/src/eu/siacs/conversations/services/EventReceiver.java
@@ -15,7 +15,8 @@ public class EventReceiver extends BroadcastReceiver {
} else {
mIntentForService.setAction("other");
}
- if (intent.getAction().equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
+ if (intent.getAction().equals("ui")
+ || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
context.startService(mIntentForService);
}
}
diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java
index af8ab4b2..ac78a454 100644
--- a/src/eu/siacs/conversations/services/ImageProvider.java
+++ b/src/eu/siacs/conversations/services/ImageProvider.java
@@ -31,7 +31,7 @@ public class ImageProvider extends ContentProvider {
if (uuids == null) {
throw new FileNotFoundException();
}
- String[] uuidsSplited = uuids.split("/",2);
+ String[] uuidsSplited = uuids.split("/", 2);
if (uuidsSplited.length != 3) {
throw new FileNotFoundException();
}
diff --git a/src/eu/siacs/conversations/services/NotificationService.java b/src/eu/siacs/conversations/services/NotificationService.java
new file mode 100644
index 00000000..41656707
--- /dev/null
+++ b/src/eu/siacs/conversations/services/NotificationService.java
@@ -0,0 +1,241 @@
+package eu.siacs.conversations.services;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.text.Html;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.ui.ConversationActivity;
+
+public class NotificationService {
+
+ private XmppConnectionService mXmppConnectionService;
+ private NotificationManager mNotificationManager;
+
+ private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
+
+ public int NOTIFICATION_ID = 0x2342;
+ private Conversation mOpenConversation;
+ private boolean mIsInForeground;
+
+ private long mEndGracePeriod = 0L;
+
+ public NotificationService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ this.mNotificationManager = (NotificationManager) service
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ public synchronized void push(Message message) {
+
+ PowerManager pm = (PowerManager) mXmppConnectionService
+ .getSystemService(Context.POWER_SERVICE);
+ boolean isScreenOn = pm.isScreenOn();
+ if (this.mIsInForeground && isScreenOn
+ && this.mOpenConversation == message.getConversation()) {
+ return;
+ }
+ String conversationUuid = message.getConversationUuid();
+ if (notifications.containsKey(conversationUuid)) {
+ notifications.get(conversationUuid).add(message);
+ } else {
+ ArrayList<Message> mList = new ArrayList<Message>();
+ mList.add(message);
+ notifications.put(conversationUuid, mList);
+ }
+ updateNotification((!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
+ && !inGracePeriod());
+ }
+
+ public void clear() {
+ notifications.clear();
+ updateNotification(false);
+ }
+
+ public void clear(Conversation conversation) {
+ notifications.remove(conversation.getUuid());
+ updateNotification(false);
+ }
+
+ private void updateNotification(boolean notify) {
+ SharedPreferences preferences = mXmppConnectionService.getPreferences();
+
+ String ringtone = preferences.getString("notification_ringtone", null);
+ boolean vibrate = preferences.getBoolean("vibrate_on_notification",
+ true);
+
+ if (notifications.size() == 0) {
+ mNotificationManager.cancel(NOTIFICATION_ID);
+ } else {
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
+ mXmppConnectionService);
+ mBuilder.setSmallIcon(R.drawable.ic_notification);
+ if (notifications.size() == 1) {
+ ArrayList<Message> messages = notifications.values().iterator()
+ .next();
+ if (messages.size() >= 1) {
+ Conversation conversation = messages.get(0)
+ .getConversation();
+ mBuilder.setLargeIcon(conversation.getImage(
+ mXmppConnectionService, 64));
+ mBuilder.setContentTitle(conversation.getName());
+ StringBuilder text = new StringBuilder();
+ for (int i = 0; i < messages.size(); ++i) {
+ text.append(messages.get(i).getReadableBody(
+ mXmppConnectionService));
+ if (i != messages.size() - 1) {
+ text.append("\n");
+ }
+ }
+ mBuilder.setStyle(new NotificationCompat.BigTextStyle()
+ .bigText(text.toString()));
+ mBuilder.setContentText(messages.get(0).getReadableBody(
+ mXmppConnectionService));
+ if (notify) {
+ mBuilder.setTicker(messages.get(messages.size() - 1)
+ .getReadableBody(mXmppConnectionService));
+ }
+ mBuilder.setContentIntent(createContentIntent(conversation
+ .getUuid()));
+ } else {
+ mNotificationManager.cancel(NOTIFICATION_ID);
+ return;
+ }
+ } else {
+ NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
+ style.setBigContentTitle(notifications.size()
+ + " "
+ + mXmppConnectionService
+ .getString(R.string.unread_conversations));
+ StringBuilder names = new StringBuilder();
+ Conversation conversation = null;
+ for (ArrayList<Message> messages : notifications.values()) {
+ if (messages.size() > 0) {
+ conversation = messages.get(0).getConversation();
+ String name = conversation.getName();
+ style.addLine(Html.fromHtml("<b>"
+ + name
+ + "</b> "
+ + messages.get(0).getReadableBody(
+ mXmppConnectionService)));
+ names.append(name);
+ names.append(", ");
+ }
+ }
+ if (names.length() >= 2) {
+ names.delete(names.length() - 2, names.length());
+ }
+ mBuilder.setContentTitle(notifications.size()
+ + " "
+ + mXmppConnectionService
+ .getString(R.string.unread_conversations));
+ mBuilder.setContentText(names.toString());
+ mBuilder.setStyle(style);
+ if (conversation != null) {
+ mBuilder.setContentIntent(createContentIntent(conversation
+ .getUuid()));
+ }
+ }
+ if (notify) {
+ if (vibrate) {
+ int dat = 70;
+ long[] pattern = { 0, 3 * dat, dat, dat };
+ mBuilder.setVibrate(pattern);
+ }
+ if (ringtone != null) {
+ mBuilder.setSound(Uri.parse(ringtone));
+ }
+ }
+ mBuilder.setDeleteIntent(createDeleteIntent());
+ if (!inGracePeriod()) {
+ mBuilder.setLights(0xffffffff, 2000, 4000);
+ }
+ Notification notification = mBuilder.build();
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ private PendingIntent createContentIntent(String conversationUuid) {
+ TaskStackBuilder stackBuilder = TaskStackBuilder
+ .create(mXmppConnectionService);
+ stackBuilder.addParentStack(ConversationActivity.class);
+
+ Intent viewConversationIntent = new Intent(mXmppConnectionService,
+ ConversationActivity.class);
+ viewConversationIntent.setAction(Intent.ACTION_VIEW);
+ viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
+ conversationUuid);
+ viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
+
+ stackBuilder.addNextIntent(viewConversationIntent);
+
+ PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ return resultPendingIntent;
+ }
+
+ private PendingIntent createDeleteIntent() {
+ Intent intent = new Intent(mXmppConnectionService,
+ XmppConnectionService.class);
+ intent.setAction("clear_notification");
+ return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
+ }
+
+ public static boolean wasHighlightedOrPrivate(Message message) {
+ String nick = message.getConversation().getMucOptions().getActualNick();
+ Pattern highlight = generateNickHighlightPattern(nick);
+ if (message.getBody() == null || nick == null) {
+ return false;
+ }
+ Matcher m = highlight.matcher(message.getBody());
+ return (m.find() || message.getType() == Message.TYPE_PRIVATE);
+ }
+
+ private static Pattern generateNickHighlightPattern(String nick) {
+ // We expect a word boundary, i.e. space or start of string, followed by
+ // the
+ // nick (matched in case-insensitive manner), followed by optional
+ // punctuation (for example "bob: i disagree" or "how are you alice?"),
+ // followed by another word boundary.
+ return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b",
+ Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ }
+
+ public void setOpenConversation(Conversation conversation) {
+ this.mOpenConversation = conversation;
+ }
+
+ public void setIsInForeground(boolean foreground) {
+ this.mIsInForeground = foreground;
+ }
+
+ public void activateGracePeriod() {
+ this.mEndGracePeriod = SystemClock.elapsedRealtime()
+ + (Config.CARBON_GRACE_PERIOD * 1000);
+ }
+
+ public void deactivateGracePeriod() {
+ this.mEndGracePeriod = 0L;
+ }
+
+ private boolean inGracePeriod() {
+ return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
+ }
+}
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index db3ee2b9..e6297f4f 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -90,9 +90,12 @@ public class XmppConnectionService extends Service {
public long startDate;
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
private MemorizingTrustManager mMemorizingTrustManager;
+ private NotificationService mNotificationService;
+
private MessageParser mMessageParser = new MessageParser(this);
private PresenceParser mPresenceParser = new PresenceParser(this);
private IqParser mIqParser = new IqParser(this);
@@ -316,14 +319,16 @@ public class XmppConnectionService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if ((intent != null)
- && (ACTION_MERGE_PHONE_CONTACTS.equals(intent.getAction()))) {
- mergePhoneContactsWithRoster();
- return START_STICKY;
- } else if ((intent != null)
- && (Intent.ACTION_SHUTDOWN.equals(intent.getAction()))) {
- logoutAndSave();
- return START_NOT_STICKY;
+ if (intent != null && intent.getAction() != null) {
+ if (intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS)) {
+ mergePhoneContactsWithRoster();
+ return START_STICKY;
+ } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+ logoutAndSave();
+ return START_NOT_STICKY;
+ } else if (intent.getAction().equals(ACTION_CLEAR_NOTIFICATION)) {
+ mNotificationService.clear();
+ }
}
this.wakeLock.acquire();
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
@@ -401,6 +406,7 @@ public class XmppConnectionService extends Service {
this.mRandom = new SecureRandom();
this.mMemorizingTrustManager = new MemorizingTrustManager(
getApplicationContext());
+ this.mNotificationService = new NotificationService(this);
this.databaseBackend = DatabaseBackend
.getInstance(getApplicationContext());
this.fileBackend = new FileBackend(getApplicationContext());
@@ -511,7 +517,8 @@ public class XmppConnectionService extends Service {
MessagePacket packet = null;
boolean saveInDb = true;
boolean send = false;
- if (account.getStatus() == Account.STATUS_ONLINE) {
+ if (account.getStatus() == Account.STATUS_ONLINE
+ && account.getXmppConnection() != null) {
if (message.getType() == Message.TYPE_IMAGE) {
if (message.getPresence() != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
@@ -561,6 +568,10 @@ public class XmppConnectionService extends Service {
send = true;
}
}
+ if (!account.getXmppConnection().getFeatures().sm()
+ && conv.getMode() != Conversation.MODE_MULTI) {
+ message.setStatus(Message.STATUS_SEND);
+ }
} else {
message.setStatus(Message.STATUS_WAITING);
if (message.getType() == Message.TYPE_TEXT) {
@@ -586,10 +597,6 @@ public class XmppConnectionService extends Service {
}
conv.getMessages().add(message);
- if (!account.getXmppConnection().getFeatures().sm()
- && conv.getMode() != Conversation.MODE_MULTI) {
- message.setStatus(Message.STATUS_SEND);
- }
if (saveInDb) {
if (message.getEncryption() == Message.ENCRYPTION_NONE
|| saveEncryptedMessages()) {
@@ -742,7 +749,7 @@ public class XmppConnectionService extends Service {
Element query = iqPacket.query("jabber:iq:private");
Element storage = query.addChild("storage", "storage:bookmarks");
for (Bookmark bookmark : account.getBookmarks()) {
- storage.addChild(bookmark.toElement());
+ storage.addChild(bookmark);
}
sendIqPacket(account, iqPacket, null);
}
@@ -824,7 +831,7 @@ public class XmppConnectionService extends Service {
}
});
}
-
+
public int loadMoreMessages(Conversation conversation, long timestamp) {
List<Message> messages = databaseBackend.getMessages(conversation, 50,
timestamp);
@@ -851,8 +858,9 @@ public class XmppConnectionService extends Service {
public Conversation find(List<Conversation> haystack, Account account,
String jid) {
for (Conversation conversation : haystack) {
- if ((conversation.getAccount().equals(account))
- && (conversation.getContactJid().split("/",2)[0].equals(jid))) {
+ if ((account == null || conversation.getAccount().equals(account))
+ && (conversation.getContactJid().split("/", 2)[0]
+ .equals(jid))) {
return conversation;
}
}
@@ -901,10 +909,12 @@ public class XmppConnectionService extends Service {
public void archiveConversation(Conversation conversation) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.autojoin()) {
- bookmark.setAutojoin(false);
- pushBookmarks(bookmark.getAccount());
+ if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null && bookmark.autojoin()) {
+ bookmark.setAutojoin(false);
+ pushBookmarks(bookmark.getAccount());
+ }
}
leaveMuc(conversation);
} else {
@@ -963,10 +973,12 @@ public class XmppConnectionService extends Service {
public void setOnConversationListChangedListener(
OnConversationUpdate listener) {
+ this.mNotificationService.deactivateGracePeriod();
if (checkListeners()) {
switchToForeground();
}
this.mOnConversationUpdate = listener;
+ this.mNotificationService.setIsInForeground(true);
this.convChangedListenerCount++;
}
@@ -974,6 +986,7 @@ public class XmppConnectionService extends Service {
this.convChangedListenerCount--;
if (this.convChangedListenerCount == 0) {
this.mOnConversationUpdate = null;
+ this.mNotificationService.setIsInForeground(false);
if (checkListeners()) {
switchToBackground();
}
@@ -981,6 +994,7 @@ public class XmppConnectionService extends Service {
}
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
+ this.mNotificationService.deactivateGracePeriod();
if (checkListeners()) {
switchToForeground();
}
@@ -999,6 +1013,7 @@ public class XmppConnectionService extends Service {
}
public void setOnRosterUpdateListener(OnRosterUpdate listener) {
+ this.mNotificationService.deactivateGracePeriod();
if (checkListeners()) {
switchToForeground();
}
@@ -1111,13 +1126,11 @@ public class XmppConnectionService extends Service {
public void providePasswordForMuc(Conversation conversation, String password) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.getMucOptions().setPassword(password);
- if (conversation.getBookmark() != null
- && conversation.getMucOptions().isPasswordChanged()) {
- if (!conversation.getBookmark().autojoin()) {
- conversation.getBookmark().setAutojoin(true);
- }
+ if (conversation.getBookmark() != null) {
+ conversation.getBookmark().setAutojoin(true);
pushBookmarks(conversation.getAccount());
}
+ databaseBackend.updateConversation(conversation);
joinMuc(conversation);
}
}
@@ -1266,7 +1279,7 @@ public class XmppConnectionService extends Service {
}
}
}
- notifyUi(conversation, false);
+ updateConversationUi();
}
public boolean renewSymmetricKey(Conversation conversation) {
@@ -1498,6 +1511,9 @@ public class XmppConnectionService extends Service {
thread.start();
scheduleWakeupCall((int) (Config.CONNECT_TIMEOUT * 1.2),
false);
+ } else {
+ account.getRoster().clearPresences();
+ account.setXmppConnection(null);
}
}
}).start();
@@ -1523,24 +1539,34 @@ public class XmppConnectionService extends Service {
public boolean markMessage(Account account, String recipient, String uuid,
int status) {
- for (Conversation conversation : getConversations()) {
- if (conversation.getContactJid().equals(recipient)
- && conversation.getAccount().equals(account)) {
- return markMessage(conversation, uuid, status);
+ if (uuid == null) {
+ return false;
+ } else {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getContactJid().equals(recipient)
+ && conversation.getAccount().equals(account)) {
+ return markMessage(conversation, uuid, status);
+ }
}
+ return false;
}
- return false;
}
public boolean markMessage(Conversation conversation, String uuid,
int status) {
- for (Message message : conversation.getMessages()) {
- if (message.getUuid().equals(uuid)) {
- markMessage(message, status);
- return true;
+ if (uuid == null) {
+ return false;
+ } else {
+ for (Message message : conversation.getMessages()) {
+ if (uuid.equals(message.getUuid())
+ || (message.getStatus() >= Message.STATUS_SEND && uuid
+ .equals(message.getRemoteMsgId()))) {
+ markMessage(message, status);
+ return true;
+ }
}
+ return false;
}
- return false;
}
public void markMessage(Message message, int status) {
@@ -1575,15 +1601,6 @@ public class XmppConnectionService extends Service {
return getPreferences().getBoolean("indicate_received", false);
}
- public void notifyUi(Conversation conversation, boolean notify) {
- if (mOnConversationUpdate != null) {
- mOnConversationUpdate.onConversationUpdate();
- } else {
- UIHelper.updateNotification(getApplicationContext(),
- getConversations(), conversation, notify);
- }
- }
-
public void updateConversationUi() {
if (mOnConversationUpdate != null) {
mOnConversationUpdate.onConversationUpdate();
@@ -1620,15 +1637,21 @@ public class XmppConnectionService extends Service {
return null;
}
- public void markRead(Conversation conversation) {
+ public void markRead(Conversation conversation, boolean calledByUi) {
+ mNotificationService.clear(conversation);
+ String id = conversation.getLatestMarkableMessageId();
conversation.markRead();
- String id = conversation.popLatestMarkableMessageId();
- if (confirmMessages() && id != null) {
+ if (confirmMessages() && id != null && calledByUi) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid()
+ + ": sending read marker for " + conversation.getName());
Account account = conversation.getAccount();
String to = conversation.getContactJid();
this.sendMessagePacket(conversation.getAccount(),
mMessageGenerator.confirm(account, to, id));
}
+ if (!calledByUi) {
+ updateConversationUi();
+ }
}
public void failWaitingOtrMessages(Conversation conversation) {
@@ -1703,16 +1726,25 @@ public class XmppConnectionService extends Service {
}
public void sendMessagePacket(Account account, MessagePacket packet) {
- account.getXmppConnection().sendMessagePacket(packet);
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendMessagePacket(packet);
+ }
}
public void sendPresencePacket(Account account, PresencePacket packet) {
- account.getXmppConnection().sendPresencePacket(packet);
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendPresencePacket(packet);
+ }
}
public void sendIqPacket(Account account, IqPacket packet,
OnIqPacketReceived callback) {
- account.getXmppConnection().sendIqPacket(packet, callback);
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendIqPacket(packet, callback);
+ }
}
public MessageGenerator getMessageGenerator() {
@@ -1742,4 +1774,22 @@ public class XmppConnectionService extends Service {
public interface OnRosterUpdate {
public void onRosterUpdate();
}
+
+ public List<Contact> findContacts(String jid) {
+ ArrayList<Contact> contacts = new ArrayList<Contact>();
+ for (Account account : getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ Contact contact = account.getRoster()
+ .getContactAsShownInRoster(jid);
+ if (contact != null) {
+ contacts.add(contact);
+ }
+ }
+ }
+ return contacts;
+ }
+
+ public NotificationService getNotificationService() {
+ return this.mNotificationService;
+ }
}