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

This commit is contained in:
Daniel Gultsch 2014-02-01 01:25:56 +01:00
parent c3e4f0eaac
commit 43531113b7
20 changed files with 185 additions and 113 deletions

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

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

View file

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

View file

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

View file

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

View file

@ -9,6 +9,7 @@ import de.gultsch.chat.R;
import de.gultsch.chat.R.id; import de.gultsch.chat.R.id;
import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.utils.Beautifier; import de.gultsch.chat.utils.Beautifier;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.content.Context; 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_lastmsg)).setText(getItem(position).getLatestMessage());
((TextView) view.findViewById(R.id.conversation_lastupdate)) ((TextView) view.findViewById(R.id.conversation_lastupdate))
.setText(Beautifier.readableTimeDifference(getItem(position).getLatestMessageDate())); .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)) ((ImageView) view.findViewById(R.id.conversation_image))
.setImageURI(getItem(position).getProfilePhotoUri()); .setImageURI(getItem(position).getProfilePhotoUri());
return view; return view;

View file

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

View file

@ -3,18 +3,24 @@ package de.gultsch.chat.utils;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; 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 class Beautifier {
public static String readableTimeDifference(long time) { public static String readableTimeDifference(long time) {
if (time==0) { if (time == 0) {
return "just now"; return "just now";
} }
Date date = new Date(time); Date date = new Date(time);
long difference = (System.currentTimeMillis() - time) / 1000; long difference = (System.currentTimeMillis() - time) / 1000;
if (difference<60) { if (difference < 60) {
return "just now"; return "just now";
} else if (difference<60*10) { } else if (difference < 60 * 10) {
return difference / 60 + " min ago"; return difference / 60 + " min ago";
} else if (difference<60*60*24) { } else if (difference < 60 * 60 * 24) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
return sdf.format(date); return sdf.format(date);
} else { } else {
@ -22,4 +28,33 @@ public class Beautifier {
return sdf.format(date); 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;
}
} }

View file

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

View file

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

View file

@ -0,0 +1,5 @@
package de.gultsch.chat.xmpp;
public interface OnIqPacketReceived {
public void onIqPacketReceived(IqPacket packet);
}

View file

@ -0,0 +1,5 @@
package de.gultsch.chat.xmpp;
public interface OnMessagePacketReceived {
public void onMessagePacketReceived(MessagePacket packet);
}

View file

@ -0,0 +1,5 @@
package de.gultsch.chat.xmpp;
public interface OnPresencePacketReceived {
public void onPresencePacketReceived(PresencePacket packet);
}

View file

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