more code cleanup for xmpp parser. more eventy. nice unknown contact pictures

Daniel Gultsch created

Change summary

gen/de/gultsch/chat/R.java                             |   3 
ic_launcher-web.png                                    |   0 
ic_profile-web.png                                     |   0 
res/drawable-hdpi/es_slidingpane_shadow.xml            |   8 
res/drawable-mdpi/es_slidingpane_shadow.xml            |   8 
res/drawable-xxhdpi/es_slidingpane_shadow.xml          |   8 
res/drawable/es_slidingpane_shadow.xml                 |   2 
res/drawable/section_header.xml                        |   0 
res/layout/fragment_conversations_overview.xml         |   6 
src/de/gultsch/chat/entities/Contact.java              |   5 
src/de/gultsch/chat/entities/Conversation.java         |   5 
src/de/gultsch/chat/ui/ConversationActivity.java       |  11 
src/de/gultsch/chat/ui/NewConversationActivity.java    |  20 +
src/de/gultsch/chat/utils/Beautifier.java              |  43 +++
src/de/gultsch/chat/xml/Element.java                   |  19 +
src/de/gultsch/chat/xmpp/IqPacket.java                 |   7 
src/de/gultsch/chat/xmpp/OnIqPacketReceived.java       |   5 
src/de/gultsch/chat/xmpp/OnMessagePacketReceived.java  |   5 
src/de/gultsch/chat/xmpp/OnPresencePacketReceived.java |   5 
src/de/gultsch/chat/xmpp/XmppConnection.java           | 138 ++++++-----
20 files changed, 185 insertions(+), 113 deletions(-)

Detailed changes

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

@@ -34,8 +34,7 @@ public final class R {
         public static final int ic_launcher=0x7f020006;
         public static final int ic_profile=0x7f020007;
         public static final int message_border=0x7f020008;
-        public static final int profilemock=0x7f020009;
-        public static final int section_header=0x7f02000a;
+        public static final int section_header=0x7f020009;
     }
     public static final class id {
         public static final int account_confirm_password_desc=0x7f0a0011;

res/drawable-hdpi/es_slidingpane_shadow.xml 🔗

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-<gradient
-    android:centerColor="#8B0000"
-    android:endColor="#34FFDD"
-    android:startColor="#FF00FF" />
-<size android:width="5.0dp" android:height="0.5dp" />
-</shape>

res/drawable-mdpi/es_slidingpane_shadow.xml 🔗

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-<gradient
-    android:centerColor="#8B0000"
-    android:endColor="#34FFDD"
-    android:startColor="#FF00FF" />
-<size android:width="5.0dp" android:height="0.5dp" />
-</shape>

res/drawable-xxhdpi/es_slidingpane_shadow.xml 🔗

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-<gradient
-    android:centerColor="#8B0000"
-    android:endColor="#34FFDD"
-    android:startColor="#FF00FF" />
-<size android:width="5.0dp" android:height="0.5dp" />
-</shape>

res/drawable-xhdpi/es_slidingpane_shadow.xml → res/drawable/es_slidingpane_shadow.xml 🔗

@@ -2,6 +2,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
 <gradient
     android:endColor="#cccccc"
-    android:startColor="#f9f9f9" />
+    android:startColor="#eeeeee" />
 <size android:width="3.0dp" android:height="0.5dp" />
 </shape>

res/layout/fragment_conversations_overview.xml 🔗

@@ -6,7 +6,9 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="336dp"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:background="#eeeeee"
+    >
  
     <ListView
         android:id="@+id/list"
@@ -14,7 +16,7 @@
         android:layout_height="wrap_content"
         android:divider="#b5b5b5"
         android:dividerHeight="1dp"
-        android:background="#f9f9f9"
+        android:background="#eeeeee"
      />
  
 </LinearLayout>

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

@@ -20,9 +20,8 @@ public class Contact implements Serializable {
 		return this.display_name;
 	}
 
-	public Uri getProfilePhoto() {
-		if (photo == null) return null;
-		return Uri.parse(photo);
+	public String getProfilePhoto() {
+		return photo;
 	}
 
 	public String getJid() {

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

@@ -33,10 +33,9 @@ public class Conversation extends AbstractEntity {
 
 	private transient List<Message> messages = null;
 
-	public Conversation(String name, Uri profilePhoto, Account account,
+	public Conversation(String name, String profilePhoto, Account account,
 			String contactJid) {
-		this(java.util.UUID.randomUUID().toString(), name, profilePhoto
-				.toString(), account.getUuid(), contactJid, System
+		this(java.util.UUID.randomUUID().toString(), name, profilePhoto, account.getUuid(), contactJid, System
 				.currentTimeMillis(), STATUS_AVAILABLE);
 	}
 

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

@@ -9,6 +9,7 @@ import de.gultsch.chat.R;
 import de.gultsch.chat.R.id;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.utils.Beautifier;
+import android.net.Uri;
 import android.os.Bundle;
 import android.app.FragmentTransaction;
 import android.content.Context;
@@ -106,6 +107,16 @@ public class ConversationActivity extends XmppActivity {
 				((TextView) view.findViewById(R.id.conversation_lastmsg)).setText(getItem(position).getLatestMessage());
 				((TextView) view.findViewById(R.id.conversation_lastupdate))
 				.setText(Beautifier.readableTimeDifference(getItem(position).getLatestMessageDate()));
+				
+				Uri profilePhoto = getItem(position).getProfilePhotoUri();
+				ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
+				if (profilePhoto!=null) {
+					imageView.setImageURI(profilePhoto);
+				} else {
+					imageView.setImageBitmap(Beautifier.getUnknownContactPicture(getItem(position).getName(),200));
+				}
+				
+				
 				((ImageView) view.findViewById(R.id.conversation_image))
 						.setImageURI(getItem(position).getProfilePhotoUri());
 				return view;

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

@@ -9,7 +9,9 @@ import de.gultsch.chat.R;
 import de.gultsch.chat.entities.Account;
 import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
+import de.gultsch.chat.utils.Beautifier;
 import de.gultsch.chat.utils.Validator;
+import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.text.Editable;
@@ -74,8 +76,7 @@ public class NewConversationActivity extends XmppActivity {
 
 			if (Validator.isValidJid(searchString)) {
 				String name = searchString.split("@")[0];
-				Contact newContact = new Contact(name, searchString,
-						DEFAULT_PROFILE_PHOTO);
+				Contact newContact = new Contact(name, searchString,null);
 				aggregatedContacts.add(newContact);
 				contactsHeader.setText("Create new contact");
 			} else {
@@ -100,8 +101,6 @@ public class NewConversationActivity extends XmppActivity {
 			+ "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
 			+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
 			+ "\")";
-	protected static final String DEFAULT_PROFILE_PHOTO = "android.resource://de.gultsch.chat/"
-			+ R.drawable.ic_profile;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState) {
@@ -150,8 +149,13 @@ public class NewConversationActivity extends XmppActivity {
 						.setText(getItem(position).getDisplayName());
 				((TextView) view.findViewById(R.id.contact_jid))
 						.setText(getItem(position).getJid());
-				((ImageView) view.findViewById(R.id.contact_photo))
-						.setImageURI(getItem(position).getProfilePhoto());
+				String profilePhoto = getItem(position).getProfilePhoto();
+				ImageView imageView = (ImageView) view.findViewById(R.id.contact_photo);
+				if (profilePhoto!=null) {
+					imageView.setImageURI(Uri.parse(profilePhoto));
+				} else {
+					imageView.setImageBitmap(Beautifier.getUnknownContactPicture(getItem(position).getDisplayName(),90));
+				}
 				return view;
 			}
 		};
@@ -222,9 +226,9 @@ public class NewConversationActivity extends XmppActivity {
 				while (cursor.moveToNext()) {
 					String profilePhoto = cursor.getString(cursor
 							.getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI));
-					if (profilePhoto == null) {
+					/*if (profilePhoto == null) {
 						profilePhoto = DEFAULT_PROFILE_PHOTO;
-					}
+					}*/
 					Contact contact = new Contact(
 							cursor.getString(cursor
 									.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)),

src/de/gultsch/chat/utils/Beautifier.java 🔗

@@ -3,18 +3,24 @@ package de.gultsch.chat.utils;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+
 public class Beautifier {
 	public static String readableTimeDifference(long time) {
-		if (time==0) {
+		if (time == 0) {
 			return "just now";
 		}
 		Date date = new Date(time);
 		long difference = (System.currentTimeMillis() - time) / 1000;
-		if (difference<60) {
+		if (difference < 60) {
 			return "just now";
-		} else if (difference<60*10) {
+		} else if (difference < 60 * 10) {
 			return difference / 60 + " min ago";
-		} else if (difference<60*60*24) {
+		} else if (difference < 60 * 60 * 24) {
 			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
 			return sdf.format(date);
 		} else {
@@ -22,4 +28,33 @@ public class Beautifier {
 			return sdf.format(date);
 		}
 	}
+
+	public static Bitmap getUnknownContactPicture(String name, int size) {
+		String firstLetter = name.substring(0, 1).toUpperCase();
+		String centerLetter = name.substring(name.length() / 2,
+				(name.length() / 2) + 1);
+
+		int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
+				0xFFe92727 };
+
+		int color = holoColors[centerLetter.charAt(0) % holoColors.length];
+
+		Bitmap bitmap = Bitmap
+				.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+		Canvas canvas = new Canvas(bitmap);
+
+		bitmap.eraseColor(color);
+
+		Paint paint = new Paint();
+		paint.setColor(0xffe5e5e5);
+		paint.setTextSize((float) (size * 0.9));
+		paint.setAntiAlias(true);
+		Rect rect = new Rect();
+		paint.getTextBounds(firstLetter, 0, 1, rect);
+		float width = paint.measureText(firstLetter);
+		canvas.drawText(firstLetter, (size / 2) - (width / 2), (size / 2)
+				+ (rect.height() / 2), paint);
+
+		return bitmap;
+	}
 }

src/de/gultsch/chat/xml/Element.java 🔗

@@ -4,6 +4,8 @@ import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 
+import android.util.Log;
+
 public class Element {
 	protected String name;
 	protected Hashtable<String, String> attributes = new Hashtable<String, String>();
@@ -26,6 +28,15 @@ public class Element {
 		return this;
 	}
 	
+	public boolean hasChild(String name) {
+		for(Element child : this.children) {
+			if (child.getName().equals(name)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
 	public Element setAttribute(String name, String value) {
 		this.attributes.put(name, value);
 		return this;
@@ -36,6 +47,14 @@ public class Element {
 		return this;
 	}
 	
+	public String getAttribute(String name) {
+		if (this.attributes.containsKey(name)) {
+			return this.attributes.get(name);
+		} else {
+			return null;
+		}
+	}
+	
 	public String toString() {
 		StringBuilder elementOutput = new StringBuilder();
 		if ((content==null)&&(children.size() == 0)) {

src/de/gultsch/chat/xmpp/IqPacket.java 🔗

@@ -12,9 +12,8 @@ public class IqPacket extends Element {
 		super(name);
 	}
 
-	public IqPacket(String id, int type) {
+	public IqPacket(int type) {
 		super("iq");
-		this.setAttribute("id",id);
 		switch (type) {
 		case TYPE_SET:
 			this.setAttribute("type", "set");
@@ -34,4 +33,8 @@ public class IqPacket extends Element {
 		super("iq");
 	}
 
+	public String getId() {
+		return this.getAttribute("id");
+	}
+
 }

src/de/gultsch/chat/xmpp/XmppConnection.java 🔗

@@ -7,6 +7,7 @@ import java.math.BigInteger;
 import java.net.Socket;
 import java.net.UnknownHostException;
 import java.security.SecureRandom;
+import java.util.Hashtable;
 
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
@@ -35,12 +36,19 @@ public class XmppConnection implements Runnable {
 	private XmlReader tagReader;
 	private TagWriter tagWriter;
 
-	private boolean isTlsEncrypted = true;
+	private boolean isTlsEncrypted = false;
 	private boolean isAuthenticated = false;
+	private boolean shouldUseTLS = false;
+	private boolean shouldReConnect = true;
+	private boolean shouldBind = true;
+	private boolean shouldAuthenticate = true;
+	private Element streamFeatures;
 	
 	private static final int PACKET_IQ = 0;
 	private static final int PACKET_MESSAGE = 1;
 	private static final int PACKET_PRESENCE = 2;
+	
+	private Hashtable<String, OnIqPacketReceived> iqPacketCallbacks = new Hashtable<String, OnIqPacketReceived>();
 
 	public XmppConnection(Account account, PowerManager pm) {
 		this.account = account;
@@ -58,17 +66,6 @@ public class XmppConnection implements Runnable {
 			tagWriter.setOutputStream(out);
 			InputStream in = socket.getInputStream();
 			tagReader.setInputStream(in);
-		} catch (UnknownHostException e) {
-			Log.d(LOGTAG, "error during connect. unknown host");
-		} catch (IOException e) {
-			Log.d(LOGTAG, "error during connect. io exception. falscher port?");
-		}
-	}
-
-	@Override
-	public void run() {
-		connect();
-		try {
 			tagWriter.beginDocument();
 			sendStartStream();
 			Tag nextTag;
@@ -77,14 +74,25 @@ public class XmppConnection implements Runnable {
 					processStream(nextTag);
 				} else {
 					Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName());
+					return;
 				}
 			}
-		} catch (XmlPullParserException e) {
-			Log.d(LOGTAG,
-					"xml error during normal read. maybe missformed xml? "
-							+ e.getMessage());
+		} catch (UnknownHostException e) {
+			Log.d(LOGTAG, "error during connect. unknown host");
+			return;
 		} catch (IOException e) {
-			Log.d(LOGTAG, "io exception during read. connection lost?");
+			Log.d(LOGTAG, "error during connect. io exception. falscher port?");
+			return;
+		} catch (XmlPullParserException e) {
+			Log.d(LOGTAG,"xml exception "+e.getMessage());
+			return;
+		}
+	}
+
+	@Override
+	public void run() {
+		while(shouldReConnect) {
+			connect();
 		}
 	}
 
@@ -92,20 +100,11 @@ public class XmppConnection implements Runnable {
 			IOException {
 		Log.d(LOGTAG, "process Stream");
 		Tag nextTag;
-		while ((nextTag = tagReader.readTag()) != null) {
+		while (!(nextTag = tagReader.readTag()).isEnd("stream")) {
 			if (nextTag.isStart("error")) {
 				processStreamError(nextTag);
 			} else if (nextTag.isStart("features")) {
 				processStreamFeatures(nextTag);
-				if (!isTlsEncrypted) {
-					sendStartTLS();
-				}
-				if ((!isAuthenticated) && (isTlsEncrypted)) {
-					sendSaslAuth();
-				}
-				if ((isAuthenticated)&&(isTlsEncrypted)) {
-					sendBindRequest();
-				}
 			} else if (nextTag.isStart("proceed")) {
 				switchOverToTls(nextTag);
 			} else if (nextTag.isStart("success")) {
@@ -121,8 +120,6 @@ public class XmppConnection implements Runnable {
 				Log.d(LOGTAG,processMessage(nextTag).toString());
 			} else if (nextTag.isStart("presence")) {
 				Log.d(LOGTAG,processPresence(nextTag).toString());
-			} else if (nextTag.isEnd("stream")) {
-				break;
 			} else {
 				Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()
 						+ " as child of " + currentTag.getName());
@@ -159,7 +156,12 @@ public class XmppConnection implements Runnable {
 	
 
 	private IqPacket processIq(Tag currentTag) throws XmlPullParserException, IOException {
-		return (IqPacket) processPacket(currentTag,PACKET_IQ);
+		IqPacket packet = (IqPacket) processPacket(currentTag,PACKET_IQ);
+		if (iqPacketCallbacks.containsKey(packet.getId())) {
+			iqPacketCallbacks.get(packet.getId()).onIqPacketReceived(packet);
+			iqPacketCallbacks.remove(packet.getId());
+		}
+		return packet;
 	}
 	
 	private MessagePacket processMessage(Tag currentTag) throws XmlPullParserException, IOException {
@@ -212,47 +214,44 @@ public class XmppConnection implements Runnable {
 
 	private void processStreamFeatures(Tag currentTag)
 			throws XmlPullParserException, IOException {
-		Log.d(LOGTAG, "processStreamFeatures");
-		
-		Element streamFeatures = new Element("features");
-		
-		Tag nextTag = tagReader.readTag();
-		while(!nextTag.isEnd("features")) {
-			Element element = tagReader.readElement(nextTag);
-			streamFeatures.addChild(element);
-			nextTag = tagReader.readTag();
+		this.streamFeatures = tagReader.readElement(currentTag);
+		Log.d(LOGTAG,"process stream features "+streamFeatures);
+		if (this.streamFeatures.hasChild("starttls")&&shouldUseTLS) {
+			sendStartTLS();
+		}
+		if (this.streamFeatures.hasChild("mechanisms")&&shouldAuthenticate) {
+			sendSaslAuth();
+		}
+		if (this.streamFeatures.hasChild("bind")&&shouldBind) {
+			sendBindRequest();
+			if (this.streamFeatures.hasChild("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);
+				sendIqPacket(startSession, null);
+				tagWriter.writeElement(startSession);
+				tagWriter.flush();
+			}
+			Element presence = new Element("presence");
+			
+			tagWriter.writeElement(presence);
+			tagWriter.flush();
 		}
-		Log.d(LOGTAG,streamFeatures.toString());
 	}
 
 	private void sendBindRequest() throws IOException {
-		IqPacket iq = new IqPacket(nextRandomId(),IqPacket.TYPE_SET);
+		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
 		Element bind = new Element("bind");
 		bind.setAttribute("xmlns","urn:ietf:params:xml:ns:xmpp-bind");
 		iq.addChild(bind);
-		//Element resource = new Element("resource");
-		//resource.setContent("mobile");
-		//bind.addChild(resource);
-		Log.d(LOGTAG,"sending bind request: "+iq.toString());
-		tagWriter.writeElement(iq);
-		tagWriter.flush();
-		
-		
-		//technically not bind stuff
-		IqPacket startSession = new IqPacket(this.nextRandomId(), IqPacket.TYPE_SET);
-		Element session = new Element("session");
-		session.setAttribute("xmlns","urn:ietf:params:xml:ns:xmpp-session");
-		session.setContent("");
-		startSession.addChild(session);
-		
-		tagWriter.writeElement(startSession);
-		tagWriter.flush();
-		
-		Element presence = new Element("presence");
-		
-		tagWriter.writeElement(presence);
-		tagWriter.flush();
-		
+		this.sendIqPacket(iq, new OnIqPacketReceived() {	
+			@Override
+			public void onIqPacketReceived(IqPacket packet) {
+				Log.d(LOGTAG,"answer for our bind was: "+packet.toString());
+			}
+		});
 	}
 
 	private void processStreamError(Tag currentTag) {
@@ -273,4 +272,15 @@ public class XmppConnection implements Runnable {
 	private String nextRandomId() {
 		return new BigInteger(50, random).toString(32);
 	}
+	
+	public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) throws IOException {
+		String id = nextRandomId();
+		packet.setAttribute("id",id);
+		tagWriter.writeElement(packet);
+		tagWriter.flush();
+		if (callback != null) {
+			iqPacketCallbacks.put(id, callback);
+		}
+		Log.d(LOGTAG,"sending: "+packet.toString());
+	}
 }