diff options
Diffstat (limited to 'src/eu/siacs/conversations/services')
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 e2445b2a9..dfbe9db76 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 af8ab4b2c..ac78a4540 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 000000000..416567071 --- /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 db3ee2b91..e6297f4ff 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; + } } |