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_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;

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" >
<gradient
android:endColor="#cccccc"
android:startColor="#f9f9f9" />
android:startColor="#eeeeee" />
<size android:width="3.0dp" android:height="0.5dp" />
</shape>

View File

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

View File

@ -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() {

View File

@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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");
}
}

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.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());
}
}