more refactoring for presence selection. removed getTo, getFrom and getJid from Element

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/entities/Message.java               |  14 
src/main/java/eu/siacs/conversations/parser/MessageParser.java           |  33 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 439 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        |  23 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java                |  14 
src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java         |   2 
src/main/java/eu/siacs/conversations/xml/Element.java                    |  30 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java    |   2 
8 files changed, 283 insertions(+), 274 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Message.java 🔗

@@ -67,18 +67,14 @@ public class Message extends AbstractEntity {
 	}
 
 	public Message(Conversation conversation, String body, int encryption) {
-		this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
-				conversation.getContactJid(), null, body, System
-						.currentTimeMillis(), encryption,
-				Message.STATUS_UNSEND, TYPE_TEXT, null);
-		this.conversation = conversation;
+		this(conversation,body,encryption,STATUS_UNSEND);
 	}
 
-	public Message(final Conversation conversation, final Jid counterpart, final String body,
-				   final int encryption, final int status) {
+	public Message(Conversation conversation, String body, int encryption, int status) {
 		this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
-				counterpart, null, body, System.currentTimeMillis(),
-				encryption, status, TYPE_TEXT, null);
+				conversation.getContactJid().toBareJid(), null, body, System
+						.currentTimeMillis(), encryption,
+				status, TYPE_TEXT, null);
 		this.conversation = conversation;
 	}
 

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -1,8 +1,11 @@
 package eu.siacs.conversations.parser;
 
+import android.util.Log;
+
 import net.java.otr4j.session.Session;
 import net.java.otr4j.session.SessionStatus;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
@@ -30,10 +33,10 @@ public class MessageParser extends AbstractParser implements
 		String pgpBody = getPgpBody(packet);
 		Message finishedMessage;
 		if (pgpBody != null) {
-			finishedMessage = new Message(conversation, packet.getFrom(),
+			finishedMessage = new Message(conversation,
 					pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECEIVED);
 		} else {
-			finishedMessage = new Message(conversation, packet.getFrom(),
+			finishedMessage = new Message(conversation,
 					packet.getBody(), Message.ENCRYPTION_NONE,
 					Message.STATUS_RECEIVED);
 		}
@@ -110,12 +113,12 @@ public class MessageParser extends AbstractParser implements
 				conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
 				return null;
 			}
-			Message finishedMessage = new Message(conversation,
-					packet.getFrom(), body, Message.ENCRYPTION_OTR,
+			Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR,
 					Message.STATUS_RECEIVED);
 			finishedMessage.setTime(getTimestamp(packet));
 			finishedMessage.setRemoteMsgId(packet.getId());
 			finishedMessage.markable = isMarkable(packet);
+			finishedMessage.setCounterpart(from);
 			return finishedMessage;
 		} catch (Exception e) {
 			String receivedId = packet.getId();
@@ -158,14 +161,15 @@ public class MessageParser extends AbstractParser implements
 		String pgpBody = getPgpBody(packet);
 		Message finishedMessage;
 		if (pgpBody == null) {
-			finishedMessage = new Message(conversation, from,
+			finishedMessage = new Message(conversation,
 					packet.getBody(), Message.ENCRYPTION_NONE, status);
 		} else {
-			finishedMessage = new Message(conversation, from, pgpBody,
+			finishedMessage = new Message(conversation, pgpBody,
 					Message.ENCRYPTION_PGP, status);
 		}
 		finishedMessage.setRemoteMsgId(packet.getId());
 		finishedMessage.markable = isMarkable(packet);
+		finishedMessage.setCounterpart(from);
 		if (status == Message.STATUS_RECEIVED) {
 			finishedMessage.setTrueCounterpart(conversation.getMucOptions()
 					.getTrueCounterpart(from.getResourcepart()));
@@ -206,7 +210,7 @@ public class MessageParser extends AbstractParser implements
 				parseNonMessage(message, account);
 			} else if (status == Message.STATUS_SEND
 					&& message.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
-				final Jid to = message.getTo();
+				final Jid to = message.getAttributeAsJid("to");
 				if (to != null) {
 					final Conversation conversation = mXmppConnectionService.find(
 							mXmppConnectionService.getConversations(), account,
@@ -219,14 +223,14 @@ public class MessageParser extends AbstractParser implements
 			return null;
 		}
 		if (status == Message.STATUS_RECEIVED) {
-			fullJid = message.getFrom();
+			fullJid = message.getAttributeAsJid("from");
 			if (fullJid == null) {
 				return null;
 			} else {
 				updateLastseen(message, account, true);
 			}
 		} else {
-			fullJid = message.getTo();
+			fullJid = message.getAttributeAsJid("to");
 			if (fullJid == null) {
 				return null;
 			}
@@ -236,20 +240,20 @@ public class MessageParser extends AbstractParser implements
 		String pgpBody = getPgpBody(message);
 		Message finishedMessage;
 		if (pgpBody != null) {
-			finishedMessage = new Message(conversation, fullJid, pgpBody,
+			finishedMessage = new Message(conversation, pgpBody,
 					Message.ENCRYPTION_PGP, status);
 		} else {
 			String body = message.findChild("body").getContent();
-			finishedMessage = new Message(conversation, fullJid, body,
+			finishedMessage = new Message(conversation, body,
 					Message.ENCRYPTION_NONE, status);
 		}
 		finishedMessage.setTime(getTimestamp(message));
 		finishedMessage.setRemoteMsgId(message.getAttribute("id"));
 		finishedMessage.markable = isMarkable(message);
+		finishedMessage.setCounterpart(fullJid);
 		if (conversation.getMode() == Conversation.MODE_MULTI
 				&& !fullJid.isBareJid()) {
 			finishedMessage.setType(Message.TYPE_PRIVATE);
-			finishedMessage.setCounterpart(fullJid);
 			finishedMessage.setTrueCounterpart(conversation.getMucOptions()
 					.getTrueCounterpart(fullJid.getResourcepart()));
 			if (conversation.hasDuplicateMessage(finishedMessage)) {
@@ -266,7 +270,7 @@ public class MessageParser extends AbstractParser implements
 	}
 
 	private void parseNonMessage(Element packet, Account account) {
-		final Jid from = packet.getFrom();
+		final Jid from = packet.getAttributeAsJid("from");
 		if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
 			Element event = packet.findChild("event",
 					"http://jabber.org/protocol/pubsub#event");
@@ -299,7 +303,7 @@ public class MessageParser extends AbstractParser implements
 			if (x.hasChild("invite")) {
 				Conversation conversation = mXmppConnectionService
 						.findOrCreateConversation(account,
-								packet.getFrom(), true);
+								packet.getAttributeAsJid("from"), true);
 				if (!conversation.getMucOptions().online()) {
 					if (x.hasChild("password")) {
 						Element password = x.findChild("password");
@@ -479,6 +483,7 @@ public class MessageParser extends AbstractParser implements
 				&& conversation.getOtrSession() != null
 				&& !conversation.getOtrSession().getSessionID().getUserID()
 				.equals(message.getCounterpart().getResourcepart())) {
+			Log.d(Config.LOGTAG, "ending because of reasons");
 			conversation.endOtrIfNeeded();
 		}
 

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -1,5 +1,40 @@
 package eu.siacs.conversations.services;
 
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.FileObserver;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.util.LruCache;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.session.Session;
+import net.java.otr4j.session.SessionID;
+import net.java.otr4j.session.SessionStatus;
+
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpServiceConnection;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.security.SecureRandom;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -12,15 +47,7 @@ import java.util.Locale;
 import java.util.TimeZone;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-import org.openintents.openpgp.util.OpenPgpApi;
-import org.openintents.openpgp.util.OpenPgpServiceConnection;
-
 import de.duenndns.ssl.MemorizingTrustManager;
-
-import net.java.otr4j.OtrException;
-import net.java.otr4j.session.Session;
-import net.java.otr4j.session.SessionID;
-import net.java.otr4j.session.SessionStatus;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.PgpEngine;
@@ -65,49 +92,49 @@ import eu.siacs.conversations.xmpp.pep.Avatar;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-import android.annotation.SuppressLint;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.FileObserver;
-import android.os.IBinder;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
-import android.util.Log;
-import android.util.LruCache;
 
 public class XmppConnectionService extends Service {
 
-	public DatabaseBackend databaseBackend;
-	private FileBackend fileBackend = new FileBackend(this);
-
-	private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
 	public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+	private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+	private ContentObserver contactObserver = new ContentObserver(null) {
+		@Override
+		public void onChange(boolean selfChange) {
+			super.onChange(selfChange);
+			Intent intent = new Intent(getApplicationContext(),
+					XmppConnectionService.class);
+			intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
+			startService(intent);
+		}
+	};
+	private final IBinder mBinder = new XmppConnectionBinder();
+	public DatabaseBackend databaseBackend;
+	public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
 
+		@Override
+		public void onContactStatusChanged(Contact contact, boolean online) {
+			Conversation conversation = find(getConversations(), contact);
+			if (conversation != null) {
+				if (online && contact.getPresences().size() > 1) {
+					conversation.endOtrIfNeeded();
+				} else {
+					conversation.resetOtrSession();
+				}
+				if (online && (contact.getPresences().size() == 1)) {
+					sendUnsendMessages(conversation);
+				}
+			}
+		}
+	};
+	private FileBackend fileBackend = new FileBackend(this);
 	private MemorizingTrustManager mMemorizingTrustManager;
-
 	private NotificationService mNotificationService = new NotificationService(
 			this);
-
 	private MessageParser mMessageParser = new MessageParser(this);
 	private PresenceParser mPresenceParser = new PresenceParser(this);
 	private IqParser mIqParser = new IqParser(this);
 	private MessageGenerator mMessageGenerator = new MessageGenerator(this);
 	private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
-
 	private List<Account> accounts;
 	private CopyOnWriteArrayList<Conversation> conversations = null;
 	private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
@@ -115,56 +142,9 @@ public class XmppConnectionService extends Service {
 	private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
 			this);
 	private AvatarService mAvatarService = new AvatarService(this);
-
 	private OnConversationUpdate mOnConversationUpdate = null;
 	private Integer convChangedListenerCount = 0;
 	private OnAccountUpdate mOnAccountUpdate = null;
-	private Integer accountChangedListenerCount = 0;
-	private OnRosterUpdate mOnRosterUpdate = null;
-	private Integer rosterChangedListenerCount = 0;
-	public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
-
-		@Override
-		public void onContactStatusChanged(Contact contact, boolean online) {
-			Conversation conversation = find(getConversations(), contact);
-			if (conversation != null) {
-				if (online && contact.getPresences().size() > 1) {
-					conversation.endOtrIfNeeded();
-				} else {
-					conversation.resetOtrSession();
-				}
-				if (online && (contact.getPresences().size() == 1)) {
-					sendUnsendMessages(conversation);
-				}
-			}
-		}
-	};
-
-	private SecureRandom mRandom;
-
-	private ContentObserver contactObserver = new ContentObserver(null) {
-		@Override
-		public void onChange(boolean selfChange) {
-			super.onChange(selfChange);
-			Intent intent = new Intent(getApplicationContext(),
-					XmppConnectionService.class);
-			intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
-			startService(intent);
-		}
-	};
-
-	private FileObserver fileObserver = new FileObserver(
-			FileBackend.getConversationsDirectory()) {
-
-		@Override
-		public void onEvent(int event, String path) {
-			if (event == FileObserver.DELETE) {
-				markFileDeleted(path.split("\\.")[0]);
-			}
-		}
-	};
-
-	private final IBinder mBinder = new XmppConnectionBinder();
 	private OnStatusChanged statusListener = new OnStatusChanged() {
 
 		@Override
@@ -182,12 +162,12 @@ public class XmppConnectionService extends Service {
 				}
 				mJingleConnectionManager.cancelInTransmission();
 				List<Conversation> conversations = getConversations();
-                for (Conversation conversation : conversations) {
-                    if (conversation.getAccount() == account) {
-                        conversation.startOtrIfNeeded();
-                        sendUnsendMessages(conversation);
-                    }
-                }
+				for (Conversation conversation : conversations) {
+					if (conversation.getAccount() == account) {
+						conversation.startOtrIfNeeded();
+						sendUnsendMessages(conversation);
+					}
+				}
 				if (connection != null && connection.getFeatures().csi()) {
 					if (checkListeners()) {
 						Log.d(Config.LOGTAG, account.getJid().toBareJid()
@@ -225,7 +205,20 @@ public class XmppConnectionService extends Service {
 					getAccounts());
 		}
 	};
+	private Integer accountChangedListenerCount = 0;
+	private OnRosterUpdate mOnRosterUpdate = null;
+	private Integer rosterChangedListenerCount = 0;
+	private SecureRandom mRandom;
+	private FileObserver fileObserver = new FileObserver(
+			FileBackend.getConversationsDirectory()) {
 
+		@Override
+		public void onEvent(int event, String path) {
+			if (event == FileObserver.DELETE) {
+				markFileDeleted(path.split("\\.")[0]);
+			}
+		}
+	};
 	private OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
 
 		@Override
@@ -276,6 +269,8 @@ public class XmppConnectionService extends Service {
 		}
 	};
 	private LruCache<String, Bitmap> mBitmapCache;
+	private OnRenameListener renameListener = null;
+	private IqGenerator mIqGenerator = new IqGenerator(this);
 
 	public PgpEngine getPgpEngine() {
 		if (pgpServiceConnection.isBound()) {
@@ -300,7 +295,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public Message attachImageToConversation(final Conversation conversation,
-			final Uri uri, final UiCallback<Message> callback) {
+											 final Uri uri, final UiCallback<Message> callback) {
 		final Message message;
 		if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
 			message = new Message(conversation, "",
@@ -339,12 +334,6 @@ public class XmppConnectionService extends Service {
 		return find(getConversations(), account, jid);
 	}
 
-	public class XmppConnectionBinder extends Binder {
-		public XmppConnectionService getService() {
-			return XmppConnectionService.this;
-		}
-	}
-
 	@Override
 	public int onStartCommand(Intent intent, int flags, int startId) {
 		if (intent != null && intent.getAction() != null) {
@@ -395,7 +384,7 @@ public class XmppConnectionService extends Service {
 						new Thread(account.getXmppConnection()).start();
 					} else if ((account.getStatus() == Account.STATUS_CONNECTING)
 							&& ((SystemClock.elapsedRealtime() - account
-									.getXmppConnection().getLastConnect()) / 1000 >= Config.CONNECT_TIMEOUT)) {
+							.getXmppConnection().getLastConnect()) / 1000 >= Config.CONNECT_TIMEOUT)) {
 						Log.d(Config.LOGTAG, account.getJid()
 								+ ": time out during connect reconnecting");
 						reconnectAccount(account, true);
@@ -412,6 +401,10 @@ public class XmppConnectionService extends Service {
 				}
 			}
 		}
+		PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
+		if (!pm.isScreenOn()) {
+			removeStaleListeners();
+		}
 		if (wakeLock.isHeld()) {
 			try {
 				wakeLock.release();
@@ -536,9 +529,9 @@ public class XmppConnectionService extends Service {
 
 	public XmppConnection createConnection(Account account) {
 		SharedPreferences sharedPref = getPreferences();
-        account.setResource(sharedPref.getString("resource", "mobile")
-                .toLowerCase(Locale.getDefault()));
-        XmppConnection connection = new XmppConnection(account, this);
+		account.setResource(sharedPref.getString("resource", "mobile")
+				.toLowerCase(Locale.getDefault()));
+		XmppConnection connection = new XmppConnection(account, this);
 		connection.setOnMessagePacketReceivedListener(this.mMessageParser);
 		connection.setOnStatusChangedListener(this.statusListener);
 		connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
@@ -582,16 +575,10 @@ public class XmppConnectionService extends Service {
 				}
 			} else {
 				if (message.getEncryption() == Message.ENCRYPTION_OTR) {
-					if (!conv.hasValidOtrSession()&& (message.getCounterpart() != null)) {
+					if (!conv.hasValidOtrSession() && (message.getCounterpart() != null)) {
 						conv.startOtrSession(this, message.getCounterpart().getResourcepart(), true);
 						message.setStatus(Message.STATUS_WAITING);
 					} else if (conv.hasValidOtrSession()) {
-						SessionID id = conv.getOtrSession().getSessionID();
-						try {
-							message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
-						} catch (final InvalidJidException e) {
-							message.setCounterpart(null);
-						}
 						if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
 							packet = mMessageGenerator.generateOtrChat(message);
 							send = true;
@@ -631,14 +618,7 @@ public class XmppConnectionService extends Service {
 					message.setBody(decryptedBody);
 					message.setEncryption(Message.ENCRYPTION_DECRYPTED);
 				} else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
-					if (conv.hasValidOtrSession()) {
-						SessionID id = conv.getOtrSession().getSessionID();
-						try {
-							message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
-						} catch (final InvalidJidException e) {
-							message.setCounterpart(null);
-						}
-					} else if (!conv.hasValidOtrSession()
+					if (!conv.hasValidOtrSession()
 							&& message.getCounterpart() != null) {
 						conv.startOtrSession(this, message.getCounterpart().getResourcepart(), false);
 					}
@@ -750,7 +730,7 @@ public class XmppConnectionService extends Service {
 
 					@Override
 					public void onIqPacketReceived(final Account account,
-							IqPacket packet) {
+												   IqPacket packet) {
 						Element query = packet.findChild("query");
 						if (query != null) {
 							account.getRoster().markAllAsNotInRoster();
@@ -818,14 +798,14 @@ public class XmppConnectionService extends Service {
 						}
 						for (Bundle phoneContact : phoneContacts) {
 							for (Account account : accounts) {
-                                Jid jid;
-                                try {
-                                    jid = Jid.fromString(phoneContact.getString("jid"));
-                                } catch (final InvalidJidException e) {
-                                    // TODO: Warn if contact import fails here?
-                                    break;
-                                }
-                                final Contact contact = account.getRoster()
+								Jid jid;
+								try {
+									jid = Jid.fromString(phoneContact.getString("jid"));
+								} catch (final InvalidJidException e) {
+									// TODO: Warn if contact import fails here?
+									break;
+								}
+								final Contact contact = account.getRoster()
 										.getContact(jid);
 								String systemAccount = phoneContact
 										.getInt("phoneid")
@@ -893,7 +873,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void populateWithOrderedConversations(List<Conversation> list,
-			boolean includeConferences) {
+												 boolean includeConferences) {
 		list.clear();
 		if (includeConferences) {
 			list.addAll(getConversations());
@@ -944,8 +924,8 @@ public class XmppConnectionService extends Service {
 	}
 
 	public Conversation find(final List<Conversation> haystack,
-                             final Account account,
-                             final Jid jid) {
+							 final Account account,
+							 final Jid jid) {
 		for (Conversation conversation : haystack) {
 			if ((account == null || conversation.getAccount() == account)
 					&& (conversation.getContactJid().toBareJid().equals(jid.toBareJid()))) {
@@ -956,7 +936,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public Conversation findOrCreateConversation(final Account account, final Jid jid,
-			final boolean muc) {
+												 final boolean muc) {
 		Conversation conversation = find(account, jid);
 		if (conversation != null) {
 			return conversation;
@@ -1058,6 +1038,46 @@ public class XmppConnectionService extends Service {
 		UIHelper.showErrorNotification(getApplicationContext(), getAccounts());
 	}
 
+	private void removeStaleListeners() {
+		boolean removedListener = false;
+		synchronized (this.convChangedListenerCount) {
+			if (this.mOnConversationUpdate != null) {
+				this.mOnConversationUpdate = null;
+				this.convChangedListenerCount = 0;
+				this.mNotificationService.setIsInForeground(false);
+				removedListener = true;
+			}
+		}
+		synchronized (this.accountChangedListenerCount) {
+			if (this.mOnAccountUpdate != null) {
+				this.mOnAccountUpdate = null;
+				this.accountChangedListenerCount = 0;
+				removedListener = true;
+			}
+		}
+		synchronized (this.rosterChangedListenerCount) {
+			if (this.mOnRosterUpdate != null) {
+				this.mOnRosterUpdate = null;
+				this.rosterChangedListenerCount = 0;
+				removedListener = true;
+			}
+		}
+		if (removedListener) {
+			final String msg = "removed stale listeners";
+			Log.d(Config.LOGTAG, msg);
+			checkListeners();
+			try {
+				OutputStream os = openFileOutput("stacktrace.txt", MODE_PRIVATE);
+				os.write(msg.getBytes());
+				os.flush();
+				os.close();
+			} catch (final FileNotFoundException ignored) {
+
+			} catch (final IOException ignored) {
+			}
+		}
+	}
+
 	public void setOnConversationListChangedListener(
 			OnConversationUpdate listener) {
 		if (!isScreenOn()) {
@@ -1181,12 +1201,12 @@ public class XmppConnectionService extends Service {
 
 	public void connectMultiModeConversations(Account account) {
 		List<Conversation> conversations = getConversations();
-        for (Conversation conversation : conversations) {
-            if ((conversation.getMode() == Conversation.MODE_MULTI)
-                    && (conversation.getAccount() == account)) {
-                joinMuc(conversation);
-            }
-        }
+		for (Conversation conversation : conversations) {
+			if ((conversation.getMode() == Conversation.MODE_MULTI)
+					&& (conversation.getAccount() == account)) {
+				joinMuc(conversation);
+			}
+		}
 	}
 
 	public void joinMuc(Conversation conversation) {
@@ -1200,7 +1220,7 @@ public class XmppConnectionService extends Service {
 			conversation.getMucOptions().setJoinNick(nick);
 			PresencePacket packet = new PresencePacket();
 			final Jid joinJid = conversation.getMucOptions().getJoinJid();
-            packet.setTo(conversation.getMucOptions().getJoinJid());
+			packet.setTo(conversation.getMucOptions().getJoinJid());
 			Element x = new Element("x");
 			x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
 			if (conversation.getMucOptions().getPassword() != null) {
@@ -1232,9 +1252,6 @@ public class XmppConnectionService extends Service {
 		}
 	}
 
-	private OnRenameListener renameListener = null;
-	private IqGenerator mIqGenerator = new IqGenerator(this);
-
 	public void setOnRenameListener(OnRenameListener listener) {
 		this.renameListener = listener;
 	}
@@ -1324,19 +1341,19 @@ public class XmppConnectionService extends Service {
 				|| (account.getStatus() == Account.STATUS_DISABLED)) {
 			if (!force) {
 				List<Conversation> conversations = getConversations();
-                for (Conversation conversation : conversations) {
-                    if (conversation.getAccount() == account) {
-                        if (conversation.getMode() == Conversation.MODE_MULTI) {
-                            leaveMuc(conversation);
-                        } else {
-                            if (conversation.endOtrIfNeeded()) {
-                                Log.d(Config.LOGTAG, account.getJid().toBareJid()
-                                        + ": ended otr session with "
-                                        + conversation.getContactJid());
-                            }
-                        }
-                    }
-                }
+				for (Conversation conversation : conversations) {
+					if (conversation.getAccount() == account) {
+						if (conversation.getMode() == Conversation.MODE_MULTI) {
+							leaveMuc(conversation);
+						} else {
+							if (conversation.endOtrIfNeeded()) {
+								Log.d(Config.LOGTAG, account.getJid().toBareJid()
+										+ ": ended otr session with "
+										+ conversation.getContactJid());
+							}
+						}
+					}
+				}
 			}
 			account.getXmppConnection().disconnect(force);
 		}
@@ -1381,28 +1398,28 @@ public class XmppConnectionService extends Service {
 				account.getJid().toBareJid() + " otr session established with "
 						+ conversation.getContactJid() + "/"
 						+ otrSession.getSessionID().getUserID());
-        for (Message msg : messages) {
-            if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING)
-                    && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
+		for (Message msg : messages) {
+			if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING)
+					&& (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
 				SessionID id = otrSession.getSessionID();
 				try {
-					msg.setCounterpart(Jid.fromString(id.getAccountID()+"/"+id.getUserID()));
+					msg.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
 				} catch (InvalidJidException e) {
 					break;
 				}
-                if (msg.getType() == Message.TYPE_TEXT) {
-                    MessagePacket outPacket = mMessageGenerator
-                            .generateOtrChat(msg, true);
-                    if (outPacket != null) {
-                        msg.setStatus(Message.STATUS_SEND);
-                        databaseBackend.updateMessage(msg);
-                        sendMessagePacket(account, outPacket);
-                    }
-                } else if (msg.getType() == Message.TYPE_IMAGE) {
-                    mJingleConnectionManager.createNewConnection(msg);
-                }
-            }
-        }
+				if (msg.getType() == Message.TYPE_TEXT) {
+					MessagePacket outPacket = mMessageGenerator
+							.generateOtrChat(msg, true);
+					if (outPacket != null) {
+						msg.setStatus(Message.STATUS_SEND);
+						databaseBackend.updateMessage(msg);
+						sendMessagePacket(account, outPacket);
+					}
+				} else if (msg.getType() == Message.TYPE_IMAGE) {
+					mJingleConnectionManager.createNewConnection(msg);
+				}
+			}
+		}
 		updateConversationUi();
 	}
 
@@ -1457,7 +1474,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void publishAvatar(Account account, Uri image,
-			final UiCallback<Avatar> callback) {
+							  final UiCallback<Avatar> callback) {
 		final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
 		final int size = Config.AVATAR_SIZE;
 		final Avatar avatar = getFileBackend()
@@ -1488,7 +1505,7 @@ public class XmppConnectionService extends Service {
 
 							@Override
 							public void onIqPacketReceived(Account account,
-									IqPacket result) {
+														   IqPacket result) {
 								if (result.getType() == IqPacket.TYPE_RESULT) {
 									if (account.setAvatar(avatar.getFilename())) {
 										databaseBackend.updateAccount(account);
@@ -1518,7 +1535,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void fetchAvatar(Account account, final Avatar avatar,
-			final UiCallback<Avatar> callback) {
+							final UiCallback<Avatar> callback) {
 		IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
 		sendIqPacket(account, packet, new OnIqPacketReceived() {
 
@@ -1574,7 +1591,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void checkForAvatar(Account account,
-			final UiCallback<Avatar> callback) {
+							   final UiCallback<Avatar> callback) {
 		IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
 		this.sendIqPacket(account, packet, new OnIqPacketReceived() {
 
@@ -1669,7 +1686,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public boolean markMessage(final Account account, final Jid recipient, final String uuid,
-			final int status) {
+							   final int status) {
 		if (uuid == null) {
 			return false;
 		} else {
@@ -1684,14 +1701,14 @@ public class XmppConnectionService extends Service {
 	}
 
 	public boolean markMessage(Conversation conversation, String uuid,
-			int status) {
+							   int status) {
 		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()))) {
+						.equals(message.getRemoteMsgId()))) {
 					markMessage(message, status);
 					return true;
 				}
@@ -1703,7 +1720,7 @@ public class XmppConnectionService extends Service {
 	public void markMessage(Message message, int status) {
 		if (status == Message.STATUS_SEND_FAILED
 				&& (message.getStatus() == Message.STATUS_SEND_RECEIVED || message
-						.getStatus() == Message.STATUS_SEND_DISPLAYED)) {
+				.getStatus() == Message.STATUS_SEND_DISPLAYED)) {
 			return;
 		}
 		message.setStatus(status);
@@ -1875,7 +1892,7 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void sendIqPacket(Account account, IqPacket packet,
-			OnIqPacketReceived callback) {
+							 OnIqPacketReceived callback) {
 		XmppConnection connection = account.getXmppConnection();
 		if (connection != null) {
 			connection.sendIqPacket(packet, callback);
@@ -1898,18 +1915,6 @@ public class XmppConnectionService extends Service {
 		return this.mJingleConnectionManager;
 	}
 
-	public interface OnConversationUpdate {
-		public void onConversationUpdate();
-	}
-
-	public interface OnAccountUpdate {
-		public void onAccountUpdate();
-	}
-
-	public interface OnRosterUpdate {
-		public void onRosterUpdate();
-	}
-
 	public List<Contact> findContacts(String jid) {
 		ArrayList<Contact> contacts = new ArrayList<>();
 		for (Account account : getAccounts()) {
@@ -1931,6 +1936,41 @@ public class XmppConnectionService extends Service {
 		return this.mHttpConnectionManager;
 	}
 
+	public void resendFailedMessages(Message message) {
+		List<Message> messages = new ArrayList<>();
+		Message current = message;
+		while (current.getStatus() == Message.STATUS_SEND_FAILED) {
+			messages.add(current);
+			if (current.mergeable(current.next())) {
+				current = current.next();
+			} else {
+				break;
+			}
+		}
+		for (Message msg : messages) {
+			markMessage(msg, Message.STATUS_WAITING);
+			this.resendMessage(msg);
+		}
+	}
+
+	public interface OnConversationUpdate {
+		public void onConversationUpdate();
+	}
+
+	public interface OnAccountUpdate {
+		public void onAccountUpdate();
+	}
+
+	public interface OnRosterUpdate {
+		public void onRosterUpdate();
+	}
+
+	public class XmppConnectionBinder extends Binder {
+		public XmppConnectionService getService() {
+			return XmppConnectionService.this;
+		}
+	}
+
 	private class DeletedDownloadable implements Downloadable {
 
 		@Override
@@ -1949,21 +1989,4 @@ public class XmppConnectionService extends Service {
 		}
 
 	}
-
-	public void resendFailedMessages(Message message) {
-		List<Message> messages = new ArrayList<>();
-		Message current = message;
-		while(current.getStatus() == Message.STATUS_SEND_FAILED) {
-			messages.add(current);
-			if (current.mergeable(current.next())) {
-				current = current.next();
-			} else {
-				break;
-			}
-		}
-		for(Message msg: messages) {
-			markMessage(msg, Message.STATUS_WAITING);
-			this.resendMessage(msg);
-		}
-	}
 }

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -825,21 +825,16 @@ public class ConversationFragment extends Fragment {
 	protected void sendOtrMessage(final Message message) {
 		final ConversationActivity activity = (ConversationActivity) getActivity();
 		final XmppConnectionService xmppService = activity.xmppConnectionService;
-		if (conversation.hasValidOtrSession()) {
-			activity.xmppConnectionService.sendMessage(message);
-			messageSent();
-		} else {
-			activity.selectPresence(message.getConversation(),
-					new OnPresenceSelected() {
+		activity.selectPresence(message.getConversation(),
+			new OnPresenceSelected() {
 
-						@Override
-						public void onPresenceSelected() {
-							message.setCounterpart(conversation.getNextCounterpart());
-							xmppService.sendMessage(message);
-							messageSent();
-						}
-					});
-		}
+				@Override
+				public void onPresenceSelected() {
+					message.setCounterpart(conversation.getNextCounterpart());
+					xmppService.sendMessage(message);
+					messageSent();
+				}
+			});
 	}
 
 	public void appendText(String text) {

src/main/java/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -48,6 +48,8 @@ import com.google.zxing.common.BitMatrix;
 import com.google.zxing.qrcode.QRCodeWriter;
 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
 
+import net.java.otr4j.session.SessionID;
+
 import java.io.FileNotFoundException;
 import java.lang.ref.WeakReference;
 import java.util.Hashtable;
@@ -452,7 +454,17 @@ public abstract class XmppActivity extends Activity {
 	public void selectPresence(final Conversation conversation,
 							   final OnPresenceSelected listener) {
 		final Contact contact = conversation.getContact();
-		if (!contact.showInRoster()) {
+		if (conversation.hasValidOtrSession()) {
+			SessionID id = conversation.getOtrSession().getSessionID();
+			Jid jid;
+			try {
+				jid = Jid.fromString(id.getAccountID() + "/" + id.getUserID());
+			} catch (InvalidJidException e) {
+				jid = null;
+			}
+			conversation.setNextCounterpart(jid);
+			listener.onPresenceSelected();
+		} else 	if (!contact.showInRoster()) {
 			showAddToRosterDialog(conversation);
 		} else {
 			Presences presences = contact.getPresences();

src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java 🔗

@@ -31,6 +31,8 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
 			OutputStream os = context.openFileOutput("stacktrace.txt",
 					Context.MODE_PRIVATE);
 			os.write(stacktrace.getBytes());
+			os.flush();
+			os.close();
 		} catch (FileNotFoundException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();

src/main/java/eu/siacs/conversations/xml/Element.java 🔗

@@ -105,8 +105,8 @@ public class Element {
 		}
 	}
 
-    public Jid getJid() {
-        final String jid = this.getAttribute("jid");
+	public Jid getAttributeAsJid(String name) {
+		final String jid = this.getAttribute(name);
         if (jid != null && !jid.isEmpty()) {
             try {
                 return Jid.fromString(jid);
@@ -115,31 +115,7 @@ public class Element {
             }
         }
         return null;
-    }
-
-    public Jid getTo() {
-        final String to = this.getAttribute("to");
-        if (to != null && !to.isEmpty()) {
-            try {
-                return Jid.fromString(to);
-            } catch (final InvalidJidException e) {
-                return null;
-            }
-        }
-        return null;
-    }
-
-    public Jid getFrom() {
-        final String from = this.getAttribute("from");
-        if (from != null && !from.isEmpty()) {
-            try {
-                return Jid.fromString(from);
-            } catch (final InvalidJidException e) {
-                return null;
-            }
-        }
-        return null;
-    }
+	}
 
 	public Hashtable<String, String> getAttributes() {
 		return this.attributes;

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java 🔗

@@ -109,7 +109,7 @@ public class JingleCandidate {
 		JingleCandidate parsedCandidate = new JingleCandidate(
 				candidate.getAttribute("cid"), false);
 		parsedCandidate.setHost(candidate.getAttribute("host"));
-		parsedCandidate.setJid(candidate.getJid());
+		parsedCandidate.setJid(candidate.getAttributeAsJid("jid"));
 		parsedCandidate.setType(candidate.getAttribute("type"));
 		parsedCandidate.setPriority(Integer.parseInt(candidate
 				.getAttribute("priority")));