Merge branch 'development'

Daniel Gultsch created

Change summary

res/values/arrays.xml                                          | 19 
res/xml/preferences.xml                                        | 16 
src/eu/siacs/conversations/crypto/OtrEngine.java               |  7 
src/eu/siacs/conversations/entities/Account.java               |  6 
src/eu/siacs/conversations/entities/Conversation.java          |  1 
src/eu/siacs/conversations/services/XmppConnectionService.java | 48 -
src/eu/siacs/conversations/ui/ConversationFragment.java        | 84 ++-
src/eu/siacs/conversations/utils/MessageParser.java            | 10 
src/eu/siacs/conversations/utils/UIHelper.java                 | 20 
src/eu/siacs/conversations/xml/Element.java                    | 17 
src/eu/siacs/conversations/xmpp/XmppConnection.java            | 37 -
src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java          |  4 
12 files changed, 131 insertions(+), 138 deletions(-)

Detailed changes

res/values/arrays.xml 🔗

@@ -1,17 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <array name="conversation_encryption_type_entries">
-        <item>None</item>
-        <item>OpenPGP</item>
-        <item>Off the record</item>
-    </array>
-    <array name="conversation_encryption_type_values">
-        <item>none</item>
-        <item>pgp</item>
-        <item>otr</item>
-    </array>
-    <array name="manage_account_options">
-        <item>Delete</item>
-        <item>Disable</item>
+    <array name="resources">
+        <item>Mobile</item>
+        <item>Phone</item>
+        <item>Tablet</item>
+        <item>Conversations</item>
+        <item>Android</item>
     </array>
 </resources>

res/xml/preferences.xml 🔗

@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
     <PreferenceCategory 
-        android:title="Security and Privacy Options">
-        <ListPreference
-            android:key="default_conversation_encryption_type"
-            android:title="Default conversation encryption"
-            android:dialogTitle="Default conversation encryption"
-            android:entries="@array/conversation_encryption_type_entries"
-            android:entryValues="@array/conversation_encryption_type_values"
-            android:defaultValue="none"/>
+        android:title="General">
         <CheckBoxPreference 
             android:key="grant_new_contacts"
             android:title="Grant presence updates"
             android:summary="Preemptivly grant and ask for presence subscription for contacts you created"
             android:defaultValue="true"
             />
+        <ListPreference 
+            android:key="resource"
+            android:title="XMPP Resource"
+            android:summary="The name this client identifies itself"
+            android:entries="@array/resources"
+            android:entryValues="@array/resources"
+            android:defaultValue="Mobile"/>
     </PreferenceCategory>
     <PreferenceCategory 
         android:title="Notification Settings">

src/eu/siacs/conversations/crypto/OtrEngine.java 🔗

@@ -158,11 +158,10 @@ public class OtrEngine implements OtrEngineHost {
 		packet.setFrom(account.getFullJid()); //sender
 		packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient
 		packet.setBody(body);
-		Element privateTag = new Element("private");
-		privateTag.setAttribute("xmlns","urn:xmpp:carbons:2");
-		packet.addChild(privateTag);
+		packet.addChild("private","urn:xmpp:carbons:2");
+		packet.addChild("no-copy","urn:xmpp:hints");
 		packet.setType(MessagePacket.TYPE_CHAT);
-		Log.d(LOGTAG,packet.toString());
+		//Log.d(LOGTAG,packet.toString());
 		account.getXmppConnection().sendMessagePacket(packet);
 	}
 

src/eu/siacs/conversations/entities/Account.java 🔗

@@ -52,7 +52,7 @@ public class Account  extends AbstractEntity{
 	protected String password;
 	protected int options = 0;
 	protected String rosterVersion;
-	protected String resource;
+	protected String resource = "mobile";
 	protected int status = -1;
 	protected JSONObject keys = new JSONObject();
 	
@@ -137,6 +137,10 @@ public class Account  extends AbstractEntity{
 		this.resource = resource;
 	}
 	
+	public String getResource() {
+		return this.resource;
+	}
+	
 	public String getJid() {
 		return username+"@"+server;
 	}

src/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -219,7 +219,6 @@ public class Conversation extends AbstractEntity {
 	}
 
 	public void startOtrSession(Context context, String presence) {
-		Log.d("xmppService", "starting otr session with " + presence);
 		SessionID sessionId = new SessionID(this.getContactJid(), presence,
 				"xmpp");
 		this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine(

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

@@ -7,6 +7,7 @@ import java.util.Comparator;
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Random;
 
 import org.openintents.openpgp.util.OpenPgpApi;
@@ -203,6 +204,12 @@ public class XmppConnectionService extends Service {
 				accountChangedListener.onAccountListChangedListener();
 			}
 			if (account.getStatus() == Account.STATUS_ONLINE) {
+				List<Conversation> conversations = getConversations();
+				for (int i = 0; i < conversations.size(); ++i) {
+					if (conversations.get(i).getAccount() == account) {
+						sendUnsendMessages(conversations.get(i));
+					}
+				}
 				scheduleWakeupCall(PING_MAX_INTERVAL, true);
 			} else if (account.getStatus() == Account.STATUS_OFFLINE) {
 				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
@@ -544,6 +551,9 @@ public class XmppConnectionService extends Service {
 	}
 
 	public XmppConnection createConnection(Account account) {
+		SharedPreferences sharedPref = PreferenceManager
+				.getDefaultSharedPreferences(getApplicationContext());
+		account.setResource(sharedPref.getString("resource", "mobile").toLowerCase(Locale.getDefault()));
 		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 		XmppConnection connection = new XmppConnection(account, pm);
 		connection.setOnMessagePacketReceivedListener(this.messageListener);
@@ -574,12 +584,6 @@ public class XmppConnectionService extends Service {
 					updateRoster(account, null);
 				}
 				connectMultiModeConversations(account);
-				List<Conversation> conversations = getConversations();
-				for (int i = 0; i < conversations.size(); ++i) {
-					if (conversations.get(i).getAccount() == account) {
-						sendUnsendMessages(conversations.get(i));
-					}
-				}
 				if (convChangedListener != null) {
 					convChangedListener.onConversationListChanged();
 				}
@@ -588,7 +592,7 @@ public class XmppConnectionService extends Service {
 		return connection;
 	}
 
-	public void sendMessage(Message message, String presence) {
+	synchronized public void sendMessage(Message message, String presence) {
 		Account account = message.getConversation().getAccount();
 		Conversation conv = message.getConversation();
 		boolean saveInDb = false;
@@ -619,11 +623,8 @@ public class XmppConnectionService extends Service {
 						.getFullJid());
 				packet.setTo(message.getCounterpart());
 				packet.setBody("This is an XEP-0027 encryted message");
-				Element x = new Element("x");
-				x.setAttribute("xmlns", "jabber:x:encrypted");
-				x.setContent(this.getPgpEngine().encrypt(keyId,
+				packet.addChild("x","jabber:x:encrypted").setContent(this.getPgpEngine().encrypt(keyId,
 						message.getBody()));
-				packet.addChild(x);
 				account.getXmppConnection().sendMessagePacket(packet);
 				message.setStatus(Message.STATUS_SEND);
 				message.setEncryption(Message.ENCRYPTION_DECRYPTED);
@@ -661,7 +662,7 @@ public class XmppConnectionService extends Service {
 
 	private void sendUnsendMessages(Conversation conversation) {
 		for (int i = 0; i < conversation.getMessages().size(); ++i) {
-			if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
+			if ((conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND)&&(conversation.getMessages().get(i).getEncryption() == Message.ENCRYPTION_NONE)) {
 				Message message = conversation.getMessages().get(i);
 				MessagePacket packet = prepareMessagePacket(
 						conversation.getAccount(), message, null);
@@ -694,9 +695,8 @@ public class XmppConnectionService extends Service {
 									+ ": could not encrypt message to "
 									+ message.getCounterpart());
 				}
-				Element privateMarker = new Element("private");
-				privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
-				packet.addChild(privateMarker);
+				packet.addChild("private","urn:xmpp:carbons:2");
+				packet.addChild("no-copy","urn:xmpp:hints");
 				packet.setTo(otrSession.getSessionID().getAccountID() + "/"
 						+ otrSession.getSessionID().getUserID());
 				packet.setFrom(account.getFullJid());
@@ -736,16 +736,13 @@ public class XmppConnectionService extends Service {
 	public void updateRoster(final Account account,
 			final OnRosterFetchedListener listener) {
 		IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
-		Element query = new Element("query");
-		query.setAttribute("xmlns", "jabber:iq:roster");
 		if (!"".equals(account.getRosterVersion())) {
 			Log.d(LOGTAG, account.getJid() + ": fetching roster version "
 					+ account.getRosterVersion());
 		} else {
 			Log.d(LOGTAG, account.getJid() + ": fetching roster");
 		}
-		query.setAttribute("ver", account.getRosterVersion());
-		iqPacket.addChild(query);
+		iqPacket.query("jabber:iq:roster").setAttribute("ver", account.getRosterVersion());
 		account.getXmppConnection().sendIqPacket(iqPacket,
 				new OnIqPacketReceived() {
 
@@ -958,13 +955,8 @@ public class XmppConnectionService extends Service {
 
 	public void deleteContact(Contact contact) {
 		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
-		Element query = new Element("query");
-		query.setAttribute("xmlns", "jabber:iq:roster");
-		Element item = new Element("item");
-		item.setAttribute("jid", contact.getJid());
-		item.setAttribute("subscription", "remove");
-		query.addChild(item);
-		iq.addChild(query);
+		Element query = iq.query("jabber:iq:roster");
+		query.addChild("item").setAttribute("jid", contact.getJid()).setAttribute("subscription", "remove");
 		contact.getAccount().getXmppConnection().sendIqPacket(iq, null);
 		replaceContactInConversation(contact.getJid(), null);
 		databaseBackend.deleteContact(contact);
@@ -1032,11 +1024,9 @@ public class XmppConnectionService extends Service {
 		Element x = new Element("x");
 		x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
 		if (conversation.getMessages().size() != 0) {
-			Element history = new Element("history");
 			long lastMsgTime = conversation.getLatestMessage().getTimeSent();
 			long diff = (System.currentTimeMillis() - lastMsgTime) / 1000 - 1;
-			history.setAttribute("seconds", diff + "");
-			x.addChild(history);
+			x.addChild("history").setAttribute("seconds", diff + "");
 		}
 		packet.addChild(x);
 		conversation.getAccount().getXmppConnection()

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

@@ -388,52 +388,54 @@ public class ConversationFragment extends Fragment {
 
 	public void updateMessages() {
 		ConversationActivity activity = (ConversationActivity) getActivity();
-		List<Message> encryptedMessages = new LinkedList<Message>();
-		for (Message message : this.conversation.getMessages()) {
-			if (message.getEncryption() == Message.ENCRYPTION_PGP) {
-				encryptedMessages.add(message);
-			}
-		}
-		if (encryptedMessages.size() > 0) {
-			DecryptMessage task = new DecryptMessage();
-			Message[] msgs = new Message[encryptedMessages.size()];
-			task.execute(encryptedMessages.toArray(msgs));
-		}
-		this.messageList.clear();
-		this.messageList.addAll(this.conversation.getMessages());
-		this.messageListAdapter.notifyDataSetChanged();
-		if (conversation.getMode() == Conversation.MODE_SINGLE) {
-			if (messageList.size() >= 1) {
-				int latestEncryption = this.conversation.getLatestMessage()
-						.getEncryption();
-				if (latestEncryption == Message.ENCRYPTION_DECRYPTED) {
-					conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
-				} else {
-					conversation.nextMessageEncryption = latestEncryption;
+		if (this.conversation != null) {
+			List<Message> encryptedMessages = new LinkedList<Message>();
+			for (Message message : this.conversation.getMessages()) {
+				if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+					encryptedMessages.add(message);
 				}
-				makeFingerprintWarning(latestEncryption);
 			}
-		} else {
-			if (conversation.getMucOptions().getError() != 0) {
-				mucError.setVisibility(View.VISIBLE);
-				if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
-					mucErrorText.setText(getString(R.string.nick_in_use));
+			if (encryptedMessages.size() > 0) {
+				DecryptMessage task = new DecryptMessage();
+				Message[] msgs = new Message[encryptedMessages.size()];
+				task.execute(encryptedMessages.toArray(msgs));
+			}
+			this.messageList.clear();
+			this.messageList.addAll(this.conversation.getMessages());
+			this.messageListAdapter.notifyDataSetChanged();
+			if (conversation.getMode() == Conversation.MODE_SINGLE) {
+				if (messageList.size() >= 1) {
+					int latestEncryption = this.conversation.getLatestMessage()
+							.getEncryption();
+					if (latestEncryption == Message.ENCRYPTION_DECRYPTED) {
+						conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
+					} else {
+						conversation.nextMessageEncryption = latestEncryption;
+					}
+					makeFingerprintWarning(latestEncryption);
 				}
 			} else {
-				mucError.setVisibility(View.GONE);
+				if (conversation.getMucOptions().getError() != 0) {
+					mucError.setVisibility(View.VISIBLE);
+					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
+						mucErrorText.setText(getString(R.string.nick_in_use));
+					}
+				} else {
+					mucError.setVisibility(View.GONE);
+				}
+			}
+			getActivity().invalidateOptionsMenu();
+			updateChatMsgHint();
+			int size = this.messageList.size();
+			if (size >= 1)
+				messagesView.setSelection(size - 1);
+			if (!activity.shouldPaneBeOpen()) {
+				conversation.markRead();
+				// TODO update notifications
+				UIHelper.updateNotification(getActivity(),
+						activity.getConversationList(), null, false);
+				activity.updateConversationList();
 			}
-		}
-		getActivity().invalidateOptionsMenu();
-		updateChatMsgHint();
-		int size = this.messageList.size();
-		if (size >= 1)
-			messagesView.setSelection(size - 1);
-		if (!activity.shouldPaneBeOpen()) {
-			conversation.markRead();
-			// TODO update notifications
-			UIHelper.updateNotification(getActivity(),
-					activity.getConversationList(), null, false);
-			activity.updateConversationList();
 		}
 	}
 

src/eu/siacs/conversations/utils/MessageParser.java 🔗

@@ -2,7 +2,6 @@ package eu.siacs.conversations.utils;
 
 import java.util.List;
 
-import net.java.otr4j.OtrException;
 import net.java.otr4j.session.Session;
 import net.java.otr4j.session.SessionStatus;
 import android.util.Log;
@@ -31,13 +30,16 @@ public class MessageParser {
 	}
 	
 	public static Message parseOtrChat(MessagePacket packet, Account account, XmppConnectionService service) {
+		boolean justStarted = false;
 		boolean properlyAddressed = (packet.getTo().split("/").length == 2) || (account.countPresences() == 1);
 		String[] fromParts = packet.getFrom().split("/");
 		Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false);
 		String body = packet.getBody();
 		if (!conversation.hasValidOtrSession()) {
 			if (properlyAddressed) {
+				Log.d("xmppService","starting new otr session with "+packet.getFrom()+" because no valid otr session has been found");
 				conversation.startOtrSession(service.getApplicationContext(), fromParts[1]);
+				justStarted = true;
 			} else {
 				Log.d("xmppService",account.getJid()+": ignoring otr session with "+fromParts[0]);
 				return null;
@@ -47,7 +49,9 @@ public class MessageParser {
 			if (!foreignPresence.equals(fromParts[1])) {
 				conversation.resetOtrSession();
 				if (properlyAddressed) {
+					Log.d("xmppService","replacing otr session with "+packet.getFrom());
 					conversation.startOtrSession(service.getApplicationContext(), fromParts[1]);
+					justStarted = true;
 				} else {
 					return null;
 				}
@@ -84,7 +88,9 @@ public class MessageParser {
 				Log.d(LOGTAG,"otr session stoped");
 			}
 		} catch (Exception e) {
-			conversation.resetOtrSession();
+			if (!justStarted) {
+				conversation.resetOtrSession();
+			}
 			return null;
 		}
 		

src/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -305,15 +305,19 @@ public class UIHelper {
 	}
 
 	public static Bitmap getSelfContactPicture(Account account, int size, boolean showPhoneSelfContactPicture, Activity activity) {
-		Uri selfiUri = PhoneHelper.getSefliUri(activity);
-		if (selfiUri != null) {
-			try {
-				return BitmapFactory.decodeStream(activity
-						.getContentResolver().openInputStream(selfiUri));
-			} catch (FileNotFoundException e) {
-				return getUnknownContactPicture(account.getJid(), size);
+		if (showPhoneSelfContactPicture) {
+			Uri selfiUri = PhoneHelper.getSefliUri(activity);
+			if (selfiUri != null) {
+				try {
+					return BitmapFactory.decodeStream(activity
+							.getContentResolver().openInputStream(selfiUri));
+				} catch (FileNotFoundException e) {
+					return getUnknownContactPicture(account.getJid(), size);
+				}
 			}
+			return getUnknownContactPicture(account.getJid(), size);
+		} else {
+			return getUnknownContactPicture(account.getJid(), size);
 		}
-		return getUnknownContactPicture(account.getJid(), size);
 	}
 }

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

@@ -17,7 +17,22 @@ public class Element {
 	public Element addChild(Element child) {
 		this.content = null;
 		children.add(child);
-		return this;
+		return child;
+	}
+	
+	public Element addChild(String name) {
+		this.content = null;
+		Element child = new Element(name);
+		children.add(child);
+		return child;
+	}
+	
+	public Element addChild(String name, String xmlns) {
+		this.content = null;
+		Element child = new Element(name);
+		child.setAttribute("xmlns", xmlns);
+		children.add(child);
+		return child;
 	}
 	
 	public Element setContent(String content) {

src/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -461,11 +461,7 @@ public class XmppConnection implements Runnable {
 			if (this.streamFeatures.hasChild("session")) {
 				Log.d(LOGTAG,"sending session");
 				IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
-				Element session = new Element("session");
-				session.setAttribute("xmlns",
-						"urn:ietf:params:xml:ns:xmpp-session");
-				session.setContent("");
-				startSession.addChild(session);
+				startSession.addChild("session","urn:ietf:params:xml:ns:xmpp-session"); //setContent("")
 				this.sendIqPacket(startSession, null);
 			}
 		}
@@ -484,7 +480,8 @@ public class XmppConnection implements Runnable {
 					IqPacket register = new IqPacket(IqPacket.TYPE_SET);
 					Element username = new Element("username").setContent(account.getUsername());
 					Element password = new Element("password").setContent(account.getPassword());
-					register.query("jabber:iq:register").addChild(username).addChild(password);
+					register.query("jabber:iq:register").addChild(username);
+					register.query().addChild(password);
 					sendIqPacket(register, new OnIqPacketReceived() {
 						
 						@Override
@@ -515,14 +512,9 @@ public class XmppConnection implements Runnable {
 		packet.setAttribute("from", account.getFullJid());
 		if (account.getKeys().has("pgp_signature")) {
 			try {
-				String signature = account.getKeys().getString("pgp_signature");	
-				Element status = new Element("status");
-				status.setContent("online");
-				packet.addChild(status);
-				Element x = new Element("x");
-				x.setAttribute("xmlns", "jabber:x:signed");
-				x.setContent(signature);
-				packet.addChild(x);
+				String signature = account.getKeys().getString("pgp_signature");
+				packet.addChild("status").setContent("online");
+				packet.addChild("x","jabber:x:signed").setContent(signature);
 			} catch (JSONException e) {
 				//
 			}
@@ -532,12 +524,7 @@ public class XmppConnection implements Runnable {
 
 	private void sendBindRequest() throws IOException {
 		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
-		Element bind = new Element("bind");
-		bind.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
-		Element resource = new Element("resource");
-		resource.setContent("Conversations");
-		bind.addChild(resource);
-		iq.addChild(bind);
+		iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind").addChild("resource").setContent(account.getResource());
 		this.sendIqPacket(iq, new OnIqPacketReceived() {
 			@Override
 			public void onIqPacketReceived(Account account, IqPacket packet) {
@@ -601,9 +588,7 @@ public class XmppConnection implements Runnable {
 
 	private void sendEnableCarbons() {
 		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
-		Element enable = new Element("enable");
-		enable.setAttribute("xmlns", "urn:xmpp:carbons:2");
-		iq.addChild(enable);
+		iq.addChild("enable","urn:xmpp:carbons:2");
 		this.sendIqPacket(iq, new OnIqPacketReceived() {
 
 			@Override
@@ -679,10 +664,8 @@ public class XmppConnection implements Runnable {
 			tagWriter.writeStanzaAsync(new RequestPacket());
 		} else {
 			IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
-			Element ping = new Element("ping");
-			iq.setAttribute("from",account.getFullJid());
-			ping.setAttribute("xmlns", "urn:xmpp:ping");
-			iq.addChild(ping);
+			iq.setFrom(account.getFullJid());
+			iq.addChild("ping","urn:xmpp:ping");
 			this.sendIqPacket(iq, null);
 		}
 	}

src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java 🔗

@@ -1,6 +1,5 @@
 package eu.siacs.conversations.xmpp.stanzas;
 
-import android.graphics.YuvImage;
 import eu.siacs.conversations.xml.Element;
 
 
@@ -39,8 +38,7 @@ public class IqPacket extends AbstractStanza {
 	public Element query() {
 		Element query = findChild("query");
 		if (query==null) {
-			query = new Element("query");
-			addChild(query);
+			query = addChild("query");
 		}
 		return query;
 	}