presences are now somewhat stored and displayed to the user via the contact details action.

Daniel Gultsch created

Change summary

gen/de/gultsch/chat/R.java                              |  74 ++--
res/layout/dialog_contact_details.xml                   |  77 +++++
res/values/strings.xml                                  |   2 
src/de/gultsch/chat/entities/Contact.java               |  33 +
src/de/gultsch/chat/entities/Presences.java             |  76 +++++
src/de/gultsch/chat/persistance/DatabaseBackend.java    |  13 
src/de/gultsch/chat/services/XmppConnectionService.java | 159 ++++++----
src/de/gultsch/chat/ui/ConversationActivity.java        |   7 
src/de/gultsch/chat/ui/ConversationFragment.java        |   8 
src/de/gultsch/chat/ui/DialogContactDetails.java        |  82 +++++
src/de/gultsch/chat/ui/NewConversationActivity.java     |  13 
11 files changed, 428 insertions(+), 116 deletions(-)

Detailed changes

gen/de/gultsch/chat/R.java 🔗

@@ -41,24 +41,24 @@ public final class R {
         public static final int section_header=0x7f02000c;
     }
     public static final class id {
-        public static final int account_confirm_password_desc=0x7f0a0012;
-        public static final int account_delete=0x7f0a0026;
-        public static final int account_disable=0x7f0a0027;
-        public static final int account_enable=0x7f0a0028;
+        public static final int account_confirm_password_desc=0x7f0a0017;
+        public static final int account_delete=0x7f0a002b;
+        public static final int account_disable=0x7f0a002c;
+        public static final int account_enable=0x7f0a002d;
         public static final int account_jid=0x7f0a0000;
-        public static final int account_list=0x7f0a001b;
-        public static final int account_password=0x7f0a000f;
-        public static final int account_password_confirm2=0x7f0a0013;
+        public static final int account_list=0x7f0a0020;
+        public static final int account_password=0x7f0a0014;
+        public static final int account_password_confirm2=0x7f0a0018;
         public static final int account_status=0x7f0a0002;
-        public static final int account_usetls=0x7f0a0010;
-        public static final int action_accounts=0x7f0a0023;
-        public static final int action_add=0x7f0a001f;
-        public static final int action_add_account=0x7f0a0025;
-        public static final int action_archive=0x7f0a0022;
-        public static final int action_details=0x7f0a0021;
-        public static final int action_refresh_contacts=0x7f0a0029;
-        public static final int action_security=0x7f0a0020;
-        public static final int action_settings=0x7f0a0024;
+        public static final int account_usetls=0x7f0a0015;
+        public static final int action_accounts=0x7f0a0028;
+        public static final int action_add=0x7f0a0024;
+        public static final int action_add_account=0x7f0a002a;
+        public static final int action_archive=0x7f0a0027;
+        public static final int action_details=0x7f0a0026;
+        public static final int action_refresh_contacts=0x7f0a002e;
+        public static final int action_security=0x7f0a0025;
+        public static final int action_settings=0x7f0a0029;
         public static final int contactList=0x7f0a0006;
         public static final int contact_display_name=0x7f0a0008;
         public static final int contact_jid=0x7f0a0009;
@@ -68,33 +68,39 @@ public final class R {
         public static final int conversation_lastmsg=0x7f0a000c;
         public static final int conversation_lastupdate=0x7f0a000d;
         public static final int conversation_name=0x7f0a000b;
-        public static final int edit_account_register_new=0x7f0a0011;
-        public static final int list=0x7f0a0019;
-        public static final int message_body=0x7f0a001d;
-        public static final int message_photo=0x7f0a001c;
-        public static final int message_time=0x7f0a001e;
-        public static final int messages_view=0x7f0a0017;
+        public static final int details_account=0x7f0a0010;
+        public static final int details_contact_jid=0x7f0a000e;
+        public static final int details_contact_status=0x7f0a000f;
+        public static final int details_receive_presence=0x7f0a0012;
+        public static final int details_send_presence=0x7f0a0011;
+        public static final int edit_account_register_new=0x7f0a0016;
+        public static final int list=0x7f0a001e;
+        public static final int message_body=0x7f0a0022;
+        public static final int message_photo=0x7f0a0021;
+        public static final int message_time=0x7f0a0023;
+        public static final int messages_view=0x7f0a001c;
         public static final int new_conversation_search=0x7f0a0004;
         public static final int progressBar1=0x7f0a0003;
-        public static final int selected_conversation=0x7f0a001a;
-        public static final int slidingpanelayout=0x7f0a0018;
-        public static final int textSendButton=0x7f0a0016;
-        public static final int textView1=0x7f0a000e;
+        public static final int selected_conversation=0x7f0a001f;
+        public static final int slidingpanelayout=0x7f0a001d;
+        public static final int textSendButton=0x7f0a001b;
+        public static final int textView1=0x7f0a0013;
         public static final int textView2=0x7f0a0001;
-        public static final int textinput=0x7f0a0015;
-        public static final int textsend=0x7f0a0014;
+        public static final int textinput=0x7f0a001a;
+        public static final int textsend=0x7f0a0019;
     }
     public static final class layout {
         public static final int account_row=0x7f030000;
         public static final int activity_new_conversation=0x7f030001;
         public static final int contact=0x7f030002;
         public static final int conversation_list_row=0x7f030003;
-        public static final int edit_account_dialog=0x7f030004;
-        public static final int fragment_conversation=0x7f030005;
-        public static final int fragment_conversations_overview=0x7f030006;
-        public static final int manage_accounts=0x7f030007;
-        public static final int message_recieved=0x7f030008;
-        public static final int message_sent=0x7f030009;
+        public static final int dialog_contact_details=0x7f030004;
+        public static final int edit_account_dialog=0x7f030005;
+        public static final int fragment_conversation=0x7f030006;
+        public static final int fragment_conversations_overview=0x7f030007;
+        public static final int manage_accounts=0x7f030008;
+        public static final int message_recieved=0x7f030009;
+        public static final int message_sent=0x7f03000a;
     }
     public static final class menu {
         public static final int conversations=0x7f090000;

res/layout/dialog_contact_details.xml 🔗

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="8dp">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Jabber ID"
+         android:textColor="#636363"
+	        android:textSize="20sp"/>
+
+    <TextView
+        android:id="@+id/details_contact_jid"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="something@jabber.example.com"
+        android:paddingLeft="8dp"
+        android:textSize="14sp"/>
+
+    <TextView
+        android:paddingTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Status"
+         android:textColor="#636363"
+	        android:textSize="20sp"/>
+
+    <TextView
+        android:paddingLeft="8dp"
+        android:id="@+id/details_contact_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="online"
+        android:textSize="14sp"/>
+    
+     <TextView
+        android:paddingTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Your account"
+         android:textColor="#636363"
+	        android:textSize="20sp"/>
+
+    <TextView
+        android:paddingLeft="8dp"
+        android:id="@+id/details_account"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="14sp"
+        android:text="julia@jabber.example.com" />
+    
+         <TextView
+        android:paddingTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Subscription"
+         android:textColor="#636363"
+	        android:textSize="20sp"/>
+
+         <CheckBox
+             android:id="@+id/details_send_presence"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:text="Send presence updates"
+             android:textSize="14sp"/>
+
+         <CheckBox
+             android:id="@+id/details_receive_presence"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:text="Receive presence updates"
+             android:textSize="14sp"/>
+
+</LinearLayout>

res/values/strings.xml 🔗

@@ -6,7 +6,7 @@
     <string name="action_add">New conversation</string>
     <string name="action_accounts">Manage accounts</string>
     <string name="action_archive">Archive conversation</string>
-    <string name="action_details">Show details</string>
+    <string name="action_details">Contact details</string>
     <string name="action_secure">Secure conversation</string>
     <string name="action_add_account">Add account</string>
     <string name="title_activity_new_conversation">New Conversation</string>

src/de/gultsch/chat/entities/Contact.java 🔗

@@ -1,6 +1,7 @@
 package de.gultsch.chat.entities;
 
 import java.io.Serializable;
+import java.util.Hashtable;
 
 import android.content.ContentValues;
 import android.database.Cursor;
@@ -16,7 +17,7 @@ public class Contact extends AbstractEntity implements Serializable {
 	public static final String SYSTEMACCOUNT = "systemaccount";
 	public static final String PHOTOURI = "photouri";
 	public static final String OPENPGPKEY = "pgpkey";
-	public static final String LASTPRESENCE = "presence";
+	public static final String PRESENCES = "presences";
 	public static final String ACCOUNT = "accountUuid";
 
 	protected String accountUuid;
@@ -26,7 +27,7 @@ public class Contact extends AbstractEntity implements Serializable {
 	protected int systemAccount;
 	protected String photoUri;
 	protected String openPGPKey;
-	protected long lastPresence;
+	protected Presences presences = new Presences();
 
 	protected Account account;
 
@@ -44,7 +45,7 @@ public class Contact extends AbstractEntity implements Serializable {
 
 	public Contact(String uuid, String account, String displayName, String jid,
 			String subscription, String photoUri, int systemAccount,
-			String pgpKey, long lastseen) {
+			String pgpKey,String presences) {
 		this.uuid = uuid;
 		this.accountUuid = account;
 		this.displayName = displayName;
@@ -53,7 +54,7 @@ public class Contact extends AbstractEntity implements Serializable {
 		this.photoUri = photoUri;
 		this.systemAccount = systemAccount;
 		this.openPGPKey = pgpKey;
-		this.lastPresence = lastseen;
+		this.presences = Presences.fromJsonString(presences);
 	}
 
 	public String getDisplayName() {
@@ -84,7 +85,7 @@ public class Contact extends AbstractEntity implements Serializable {
 		values.put(SYSTEMACCOUNT, systemAccount);
 		values.put(PHOTOURI, photoUri);
 		values.put(OPENPGPKEY, openPGPKey);
-		values.put(LASTPRESENCE, lastPresence);
+		values.put(PRESENCES, presences.toJsonString());
 		return values;
 	}
 
@@ -97,12 +98,16 @@ public class Contact extends AbstractEntity implements Serializable {
 				cursor.getString(cursor.getColumnIndex(PHOTOURI)),
 				cursor.getInt(cursor.getColumnIndex(SYSTEMACCOUNT)),
 				cursor.getString(cursor.getColumnIndex(OPENPGPKEY)),
-				cursor.getLong(cursor.getColumnIndex(LASTPRESENCE)));
+				cursor.getString(cursor.getColumnIndex(PRESENCES)));
 	}
 
 	public void setSubscription(String subscription) {
 		this.subscription = subscription;
 	}
+	
+	public String getSubscription() {
+		return this.subscription;
+	}
 
 	public void setSystemAccount(int account) {
 		this.systemAccount = account;
@@ -136,4 +141,20 @@ public class Contact extends AbstractEntity implements Serializable {
 			}
 		}
 	}
+	
+	public Hashtable<String, Integer> getPresences() {
+		return this.presences.getPresences();
+	}
+	
+	public void updatePresence(String resource, int status) {
+		this.presences.updatePresence(resource, status);
+	}
+
+	public void removePresence(String resource) {
+		this.presences.removePresence(resource);
+	}
+	
+	public int getMostAvailableStatus() {
+		return this.presences.getMostAvailableStatus();
+	}
 }

src/de/gultsch/chat/entities/Presences.java 🔗

@@ -0,0 +1,76 @@
+package de.gultsch.chat.entities;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class Presences {
+
+	public static final int CHAT = -1;
+	public static final int ONLINE = 0;
+	public static final int AWAY = 1;
+	public static final int XA = 2;
+	public static final int DND = 3;
+	public static final int OFFLINE = 4;
+	
+	private Hashtable<String, Integer> presences = new Hashtable<String, Integer>();
+
+	public Hashtable<String, Integer> getPresences() {
+		return this.presences;
+	}
+
+	public void updatePresence(String resource, int status) {
+		this.presences.put(resource, status);
+	}
+
+	public void removePresence(String resource) {
+		this.presences.remove(resource);
+	}
+	
+	public int getMostAvailableStatus() {
+		int status = OFFLINE;
+		Iterator<Entry<String, Integer>> it = presences.entrySet().iterator();
+		while (it.hasNext()) {
+			Entry<String, Integer> entry = it.next();
+			if (entry.getValue()<status) status = entry.getValue();
+		}
+		return status;
+	}
+
+	public String toJsonString() {
+		JSONArray json = new JSONArray();
+		Iterator<Entry<String, Integer>> it = presences.entrySet().iterator();
+
+		while (it.hasNext()) {
+			Entry<String, Integer> entry = it.next();
+			JSONObject jObj = new JSONObject();
+			try {
+				jObj.put("resource", entry.getKey());
+				jObj.put("status", entry.getValue());
+			} catch (JSONException e) {
+
+			}
+			json.put(jObj);
+		}
+		return json.toString();
+	}
+
+	public static Presences fromJsonString(String jsonString) {
+		Presences presences = new Presences();
+		try {
+			JSONArray json = new JSONArray(jsonString);
+			for (int i = 0; i < json.length(); ++i) {
+				JSONObject jObj = json.getJSONObject(i);
+				presences.updatePresence(jObj.getString("resource"),
+						jObj.getInt("status"));
+			}
+		} catch (JSONException e1) {
+			
+		}
+		return presences;
+	}
+}

src/de/gultsch/chat/persistance/DatabaseBackend.java 🔗

@@ -8,10 +8,12 @@ import de.gultsch.chat.entities.Account;
 import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.entities.Message;
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Bundle;
 import android.util.Log;
 
 public class DatabaseBackend extends SQLiteOpenHelper {
@@ -53,7 +55,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 		db.execSQL("create table " + Contact.TABLENAME + "(" + Contact.UUID
 				+ " TEXT PRIMARY KEY, " + Contact.ACCOUNT + " TEXT, "
 				+ Contact.DISPLAYNAME + " TEXT," + Contact.JID + " TEXT,"
-				+ Contact.LASTPRESENCE + " NUMBER, " + Contact.OPENPGPKEY
+				+ Contact.PRESENCES + " TEXT, " + Contact.OPENPGPKEY
 				+ " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.SUBSCRIPTION
 				+ " TEXT," + Contact.SYSTEMACCOUNT + " NUMBER, "
 				+ "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
@@ -199,6 +201,15 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 				+ "=?", args);
 	}
 	
+	public void clearPresences(Account account) {
+		SQLiteDatabase db = this.getWritableDatabase();
+		String[] args = { account.getUuid() };
+		ContentValues values = new ContentValues();
+		values.put(Contact.PRESENCES,"[]");
+		db.update(Contact.TABLENAME, values, Contact.ACCOUNT
+				+ "=?", args);
+	}
+	
 	public void mergeContacts(List<Contact> contacts) {
 		SQLiteDatabase db = this.getWritableDatabase();
 		for (int i = 0; i < contacts.size(); i++) {

src/de/gultsch/chat/services/XmppConnectionService.java 🔗

@@ -8,6 +8,7 @@ import de.gultsch.chat.entities.Account;
 import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.entities.Message;
+import de.gultsch.chat.entities.Presences;
 import de.gultsch.chat.persistance.DatabaseBackend;
 import de.gultsch.chat.ui.OnAccountListChangedListener;
 import de.gultsch.chat.ui.OnConversationListChangedListener;
@@ -58,56 +59,46 @@ public class XmppConnectionService extends Service {
 		@Override
 		public void onMessagePacketReceived(Account account,
 				MessagePacket packet) {
-			Conversation conversation = null;
-			String fullJid = packet.getFrom();
-			String counterPart = null;
-			if (packet.getType() == MessagePacket.TYPE_CHAT) {
-				String jid = fullJid.split("/")[0];
-				counterPart = fullJid;
-				Contact contact = findOrCreateContact(account,jid);
-				conversation = findOrCreateConversation(account, contact,false);
-			} else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
-				String[] fromParts = fullJid.split("/");
-				if (fromParts.length != 2) {
-					return;
-				}
-				if (packet.hasChild("subject")) {
-					return;
-				}
-				if (packet.hasChild("delay")) {
+			if ((packet.getType() == MessagePacket.TYPE_CHAT)
+					|| (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
+				boolean notify = true;
+				if (!packet.hasChild("body")) {
 					return;
 				}
-
-				String muc = fromParts[0];
-				counterPart = fromParts[1];
-				if (counterPart.equals(account.getUsername())) {
-					return;
-				}
-				for (int i = 0; i < conversations.size(); ++i) {
-					if (conversations.get(i).getContactJid().equals(muc)) {
-						conversation = conversations.get(i);
-						break;
+				Conversation conversation = null;
+				String fullJid = packet.getFrom();
+				String[] fromParts = fullJid.split("/");
+				String jid = fromParts[0];
+				Contact contact = findOrCreateContact(account, jid);
+				boolean muc = (packet.getType() == MessagePacket.TYPE_GROUPCHAT);
+				String counterPart = null;
+				int status = Message.STATUS_RECIEVED;
+				conversation = findOrCreateConversation(account, contact, muc);
+				if (muc) {
+					if ((fromParts.length==1)||(packet.hasChild("subject"))||(packet.hasChild("delay"))) {
+						return;
 					}
+					counterPart = fromParts[1];
+					if (counterPart.equals(account.getUsername())) {
+						status = Message.STATUS_SEND;
+						notify = false;
+					}
+				} else {
+					counterPart = fullJid;
 				}
-				if (conversation == null) {
-					Log.d(LOGTAG, "couldnt find muc");
-				}
-
-			}
-			if (conversation != null) {
-				Log.d(LOGTAG, packet.toString());
 				Message message = new Message(conversation, counterPart,
-						packet.getBody(), Message.ENCRYPTION_NONE,
-						Message.STATUS_RECIEVED);
+						packet.getBody(), Message.ENCRYPTION_NONE, status);
 				conversation.getMessages().add(message);
 				databaseBackend.createMessage(message);
 				if (convChangedListener != null) {
 					convChangedListener.onConversationListChanged();
 				} else {
-					NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-					mNotificationManager.notify(2342, UIHelper
-							.getUnreadMessageNotification(
-									getApplicationContext(), conversation));
+					if (notify) {
+						NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+						mNotificationManager.notify(2342, UIHelper
+								.getUnreadMessageNotification(
+										getApplicationContext(), conversation));
+					}
 				}
 			}
 		}
@@ -120,19 +111,45 @@ public class XmppConnectionService extends Service {
 				accountChangedListener.onAccountListChangedListener();
 			}
 			if (account.getStatus() == Account.STATUS_ONLINE) {
+				databaseBackend.clearPresences(account);
 				connectMultiModeConversations(account);
 			}
 		}
 	};
-	
+
 	private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() {
-		
+
 		@Override
-		public void onPresencePacketReceived(Account account, PresencePacket packet) {
-			String jid = packet.getAttribute("from");
+		public void onPresencePacketReceived(Account account,
+				PresencePacket packet) {
+			String[] fromParts = packet.getAttribute("from").split("/");
+			Contact contact = findOrCreateContact(account, fromParts[0]);
+			if (contact.getUuid()==null) {
+				//most likely muc, self or roster not synced
+				Log.d(LOGTAG,"got presence for non contact "+packet.toString());
+			}
 			String type = packet.getAttribute("type");
-			if (type==null) {
-				//Log.d(LOGTAG,"online presence from "+jid);
+			if (type == null) {
+				Element show = packet.findChild("show");
+				if (show==null) {
+					contact.updatePresence(fromParts[1],Presences.ONLINE);
+				} else if (show.getContent().equals("away")) {
+					contact.updatePresence(fromParts[1],Presences.AWAY);
+				} else if (show.getContent().equals("xa")) {
+					contact.updatePresence(fromParts[1],Presences.XA);
+				} else if (show.getContent().equals("chat")) {
+					contact.updatePresence(fromParts[1],Presences.CHAT);
+				} else if (show.getContent().equals("dnd")) {
+					contact.updatePresence(fromParts[1],Presences.DND);
+				}
+				databaseBackend.updateContact(contact);
+			} else if (type.equals("unavailable")) {
+				if (fromParts.length!=2) {
+					Log.d(LOGTAG,"received presence with no resource "+packet.toString());
+				} else {
+					contact.removePresence(fromParts[1]);
+					databaseBackend.updateContact(contact);
+				}
 			}
 		}
 	};
@@ -176,11 +193,6 @@ public class XmppConnectionService extends Service {
 		return connection;
 	}
 
-	@Override
-	public IBinder onBind(Intent intent) {
-		return mBinder;
-	}
-
 	public void sendMessage(final Account account, final Message message) {
 		Log.d(LOGTAG, "sending message for " + account.getJid() + " to: "
 				+ message.getCounterpart());
@@ -194,14 +206,17 @@ public class XmppConnectionService extends Service {
 		packet.setTo(message.getCounterpart());
 		packet.setFrom(account.getJid());
 		packet.setBody(message.getBody());
-		connections.get(account).sendMessagePacket(packet);
-		message.setStatus(Message.STATUS_SEND);
-		databaseBackend.updateMessage(message);
+		if (account.getStatus()==Account.STATUS_ONLINE) {
+			connections.get(account).sendMessagePacket(packet);
+			message.setStatus(Message.STATUS_SEND);
+			databaseBackend.updateMessage(message);
+		}
 	}
-	
-	public void getRoster(Account account, final OnRosterFetchedListener listener) {
+
+	public void getRoster(Account account,
+			final OnRosterFetchedListener listener) {
 		List<Contact> contacts = databaseBackend.getContacts(account);
-		for(int i=0; i < contacts.size(); ++i) {
+		for (int i = 0; i < contacts.size(); ++i) {
 			contacts.get(i).setAccount(account);
 		}
 		if (listener != null) {
@@ -278,7 +293,8 @@ public class XmppConnectionService extends Service {
 													jid,
 													phoneContact
 															.getString("photouri"));
-											contact.setSystemAccount(phoneContact.getInt("phoneid"));
+											contact.setSystemAccount(phoneContact
+													.getInt("phoneid"));
 										} else {
 											if (name == null) {
 												name = jid.split("@")[0];
@@ -331,18 +347,19 @@ public class XmppConnectionService extends Service {
 	public List<Message> getMessages(Conversation conversation) {
 		return databaseBackend.getMessages(conversation, 100);
 	}
-	
+
 	public Contact findOrCreateContact(Account account, String jid) {
-		Contact contact = databaseBackend.findContact(account,jid);
-		if (contact!=null) {
+		Contact contact = databaseBackend.findContact(account, jid);
+		if (contact != null) {
+			contact.setAccount(account);
 			return contact;
 		} else {
-			return new Contact(account,jid.split("@")[0], jid, null);
+			return new Contact(account, jid.split("@")[0], jid, null);
 		}
 	}
 
 	public Conversation findOrCreateConversation(Account account,
-			Contact contact,boolean muc) {
+			Contact contact, boolean muc) {
 		for (Conversation conv : this.getConversations()) {
 			if ((conv.getAccount().equals(account))
 					&& (conv.getContactJid().equals(contact.getJid()))) {
@@ -356,7 +373,7 @@ public class XmppConnectionService extends Service {
 			conversation.setAccount(account);
 			if (muc) {
 				conversation.setMode(Conversation.MODE_MULTI);
-				if (account.getStatus()==Account.STATUS_ONLINE) {
+				if (account.getStatus() == Account.STATUS_ONLINE) {
 					joinMuc(account, conversation);
 				}
 			} else {
@@ -368,7 +385,7 @@ public class XmppConnectionService extends Service {
 				conversation = new Conversation(contact.getDisplayName(),
 						contact.getProfilePhoto(), account, contact.getJid(),
 						Conversation.MODE_MULTI);
-				if (account.getStatus()==Account.STATUS_ONLINE) {
+				if (account.getStatus() == Account.STATUS_ONLINE) {
 					joinMuc(account, conversation);
 				}
 			} else {
@@ -463,7 +480,7 @@ public class XmppConnectionService extends Service {
 			}
 		}
 	}
-	
+
 	public void joinMuc(Account account, Conversation conversation) {
 		String muc = conversation.getContactJid();
 		PresencePacket packet = new PresencePacket();
@@ -471,11 +488,15 @@ public class XmppConnectionService extends Service {
 		Element x = new Element("x");
 		x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
 		packet.addChild(x);
-		connections.get(conversation.getAccount()).sendPresencePacket(
-				packet);
+		connections.get(conversation.getAccount()).sendPresencePacket(packet);
 	}
 
 	public void disconnectMultiModeConversations() {
 
 	}
-}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return mBinder;
+	}
+}

src/de/gultsch/chat/ui/ConversationActivity.java 🔗

@@ -7,6 +7,7 @@ import java.util.List;
 
 import de.gultsch.chat.R;
 import de.gultsch.chat.R.id;
+import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.utils.UIHelper;
 import android.net.Uri;
@@ -257,6 +258,12 @@ public class ConversationActivity extends XmppActivity {
 			xmppConnectionService.archiveConversation(conv);
 			selectedConversation = conversationList.get(0);
 			break;
+		case R.id.action_details:
+			DialogContactDetails details = new DialogContactDetails();
+			Contact contact = xmppConnectionService.findOrCreateContact(this.getSelectedConversation().getAccount(),this.getSelectedConversation().getContactJid());
+			details.setContact(contact);
+			details.show(getFragmentManager(), "details");
+			break;
 		default:
 			break;
 		}

src/de/gultsch/chat/ui/ConversationFragment.java 🔗

@@ -59,7 +59,9 @@ public class ConversationFragment extends Fragment {
 						conversation.getMessages().add(message);
 						chatMsg.setText("");
 						
-						messageList.add(message);
+						if (conversation.getMode()==Conversation.MODE_SINGLE) {
+							messageList.add(message);
+						}
 						
 						activity.updateConversationList();
 						
@@ -155,9 +157,6 @@ public class ConversationFragment extends Fragment {
 	@Override
 	public void onStart() {
 		super.onStart();
-		
-		Log.d("gultsch","conversationfragment onStart");
-
 		final ConversationActivity activity = (ConversationActivity) getActivity();
 		
 		if (activity.xmppConnectionServiceBound) {
@@ -174,7 +173,6 @@ public class ConversationFragment extends Fragment {
 	}
 	
 	public void onBackendConnected() {
-		Log.d("gultsch","calling on backend connected in conversation fragment");
 		final ConversationActivity activity = (ConversationActivity) getActivity();
 		this.conversation = activity.getSelectedConversation();
 		updateMessages();

src/de/gultsch/chat/ui/DialogContactDetails.java 🔗

@@ -0,0 +1,82 @@
+package de.gultsch.chat.ui;
+
+import de.gultsch.chat.R;
+import de.gultsch.chat.entities.Contact;
+import de.gultsch.chat.entities.Presences;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class DialogContactDetails extends DialogFragment {
+	
+	private Contact contact = null;
+	boolean displayingInRoster = false;
+	
+	public void setContact(Contact contact) {
+		this.contact = contact;
+	}
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		LayoutInflater inflater = getActivity().getLayoutInflater();
+		View view = inflater.inflate(R.layout.dialog_contact_details, null);
+		TextView contactJid = (TextView) view.findViewById(R.id.details_contact_jid);
+		TextView accountJid = (TextView) view.findViewById(R.id.details_account);
+		TextView status = (TextView) view.findViewById(R.id.details_contact_status);
+		CheckBox send = (CheckBox) view.findViewById(R.id.details_send_presence);
+		CheckBox receive = (CheckBox) view.findViewById(R.id.details_receive_presence);
+		
+		boolean subscriptionSend = false;
+		boolean subscriptionReceive = false;
+		if (contact.getSubscription().equals("both")) {
+			subscriptionReceive = true;
+			subscriptionSend = true;
+		} else if (contact.getSubscription().equals("from")) {
+			subscriptionSend = true;
+		} else if (contact.getSubscription().equals("to")) {
+			subscriptionReceive = true;
+		}
+		
+		switch (contact.getMostAvailableStatus()) {
+		case Presences.CHAT:
+			status.setText("free to chat");
+			break;
+		case Presences.ONLINE:
+			status.setText("online");
+			break;
+		case Presences.AWAY:
+			status.setText("away");
+			break;
+		case Presences.XA:
+			status.setText("extended away");
+			break;
+		case Presences.DND:
+			status.setText("do not disturb");
+			break;
+		case Presences.OFFLINE:
+			status.setText("offline");
+			break;
+		default:
+			status.setText("offline");
+			break;
+		}
+		
+		send.setChecked(subscriptionSend);
+		receive.setChecked(subscriptionReceive);
+		contactJid.setText(contact.getJid());
+		accountJid.setText(contact.getAccount().getJid());
+		
+		builder.setView(view);
+		builder.setTitle(contact.getDisplayName());
+		
+		builder.setNeutralButton("Done", null);
+		builder.setPositiveButton("Remove from roster", null);
+		return builder.create();
+	}
+}

src/de/gultsch/chat/ui/NewConversationActivity.java 🔗

@@ -24,6 +24,7 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.EditText;
 import android.widget.ListView;
@@ -185,6 +186,18 @@ public class NewConversationActivity extends XmppActivity {
 				}
 			}
 		});
+		contactsView.setOnItemLongClickListener(new OnItemLongClickListener() {
+
+			@Override
+			public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
+					int pos, long arg3) {
+				Contact clickedContact = aggregatedContacts.get(pos);
+				DialogContactDetails dialog = new DialogContactDetails();
+				dialog.setContact(clickedContact);
+				dialog.show(getFragmentManager(), "details");
+				return true;
+			}
+		});
 	}
 	
 	public void showIsMucDialogIfNeeded(final Contact clickedContact) {