aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout/dialog_verify_otr.xml10
-rw-r--r--res/layout/fragment_conversation.xml3
-rw-r--r--src/de/gultsch/chat/entities/Account.java12
-rw-r--r--src/de/gultsch/chat/entities/Contact.java3
-rw-r--r--src/de/gultsch/chat/persistance/DatabaseBackend.java18
-rw-r--r--src/de/gultsch/chat/persistance/OnPhoneContactsMerged.java5
-rw-r--r--src/de/gultsch/chat/services/XmppConnectionService.java390
-rw-r--r--src/de/gultsch/chat/ui/ConversationFragment.java205
-rw-r--r--src/de/gultsch/chat/utils/MessageParser.java119
-rw-r--r--src/de/gultsch/chat/utils/PhoneHelper.java11
-rw-r--r--src/de/gultsch/chat/xml/XmlReader.java4
11 files changed, 491 insertions, 289 deletions
diff --git a/res/layout/dialog_verify_otr.xml b/res/layout/dialog_verify_otr.xml
index 9f389c73..41ce3b07 100644
--- a/res/layout/dialog_verify_otr.xml
+++ b/res/layout/dialog_verify_otr.xml
@@ -3,7 +3,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:paddingLeft="8dp">
+ android:paddingLeft="8dp"
+ android:paddingBottom="16dp"
+ android:paddingRight="8dp">
<TextView
android:layout_width="wrap_content"
@@ -34,7 +36,8 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:text="2674D6A0 0B1421B1 BFC42AEC C56F3719 672437D8"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:typeface="monospace"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -49,5 +52,6 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:text="2674D6A0 0B1421B1 BFC42AEC C56F3719 672437D8"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:typeface="monospace"/>
</LinearLayout>
diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml
index 5db68748..ad5f34b8 100644
--- a/res/layout/fragment_conversation.xml
+++ b/res/layout/fragment_conversation.xml
@@ -80,7 +80,8 @@
android:text="2674D6A0 0B1421B1 BFC42AEC C56F3719 672437D8"
android:paddingLeft="8dp"
android:paddingBottom="8dp"
- android:textSize="14sp"/>
+ android:textSize="14sp"
+ android:typeface="monospace"/>
</LinearLayout>
diff --git a/src/de/gultsch/chat/entities/Account.java b/src/de/gultsch/chat/entities/Account.java
index 1bced45f..dce5677e 100644
--- a/src/de/gultsch/chat/entities/Account.java
+++ b/src/de/gultsch/chat/entities/Account.java
@@ -201,4 +201,16 @@ public class Account extends AbstractEntity{
}
return this.otrFingerprint;
}
+
+ public String getRosterVersion() {
+ if (this.rosterVersion==null) {
+ return "";
+ } else {
+ return this.rosterVersion;
+ }
+ }
+
+ public void setRosterVersion(String version) {
+ this.rosterVersion = version;
+ }
}
diff --git a/src/de/gultsch/chat/entities/Contact.java b/src/de/gultsch/chat/entities/Contact.java
index 5d5710a5..c899603f 100644
--- a/src/de/gultsch/chat/entities/Contact.java
+++ b/src/de/gultsch/chat/entities/Contact.java
@@ -32,7 +32,7 @@ public class Contact extends AbstractEntity implements Serializable {
protected String subscription;
protected String systemAccount;
protected String photoUri;
- protected JSONObject keys;
+ protected JSONObject keys = new JSONObject();
protected Presences presences = new Presences();
protected Account account;
@@ -47,6 +47,7 @@ public class Contact extends AbstractEntity implements Serializable {
this.displayName = displayName;
this.jid = jid;
this.photoUri = photoUri;
+ this.uuid = java.util.UUID.randomUUID().toString();
}
public Contact(String uuid, String account, String displayName, String jid,
diff --git a/src/de/gultsch/chat/persistance/DatabaseBackend.java b/src/de/gultsch/chat/persistance/DatabaseBackend.java
index 4607883c..caee4e8f 100644
--- a/src/de/gultsch/chat/persistance/DatabaseBackend.java
+++ b/src/de/gultsch/chat/persistance/DatabaseBackend.java
@@ -245,6 +245,16 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
return list;
}
+
+ public List<Contact> getContats(String where) {
+ List<Contact> list = new ArrayList<Contact>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(Contact.TABLENAME, null, where, null, null, null, null);
+ while (cursor.moveToNext()) {
+ list.add(Contact.fromCursor(cursor));
+ }
+ return list;
+ }
public Contact findContact(Account account, String jid) {
SQLiteDatabase db = this.getReadableDatabase();
@@ -263,4 +273,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
String[] args = { message.getUuid() };
db.delete(Message.TABLENAME, Message.UUID + "=?", args);
}
+
+ public void deleteContact(Contact contact) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = { contact.getUuid() };
+ db.delete(Contact.TABLENAME, Contact.UUID + "=?", args);
+ }
+
+
}
diff --git a/src/de/gultsch/chat/persistance/OnPhoneContactsMerged.java b/src/de/gultsch/chat/persistance/OnPhoneContactsMerged.java
new file mode 100644
index 00000000..a7918efb
--- /dev/null
+++ b/src/de/gultsch/chat/persistance/OnPhoneContactsMerged.java
@@ -0,0 +1,5 @@
+package de.gultsch.chat.persistance;
+
+public interface OnPhoneContactsMerged {
+ public void phoneContactsMerged();
+}
diff --git a/src/de/gultsch/chat/services/XmppConnectionService.java b/src/de/gultsch/chat/services/XmppConnectionService.java
index 63b894ab..7111d94a 100644
--- a/src/de/gultsch/chat/services/XmppConnectionService.java
+++ b/src/de/gultsch/chat/services/XmppConnectionService.java
@@ -19,9 +19,11 @@ 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.persistance.OnPhoneContactsMerged;
import de.gultsch.chat.ui.OnAccountListChangedListener;
import de.gultsch.chat.ui.OnConversationListChangedListener;
import de.gultsch.chat.ui.OnRosterFetchedListener;
+import de.gultsch.chat.utils.MessageParser;
import de.gultsch.chat.utils.OnPhoneContactsLoadedListener;
import de.gultsch.chat.utils.PhoneHelper;
import de.gultsch.chat.utils.UIHelper;
@@ -39,6 +41,7 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
+import android.database.DatabaseUtils;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -49,14 +52,14 @@ import android.util.Log;
public class XmppConnectionService extends Service {
protected static final String LOGTAG = "xmppService";
- protected DatabaseBackend databaseBackend;
+ public DatabaseBackend databaseBackend;
public long startDate;
private List<Account> accounts;
private List<Conversation> conversations = null;
- private OnConversationListChangedListener convChangedListener = null;
+ public OnConversationListChangedListener convChangedListener = null;
private OnAccountListChangedListener accountChangedListener = null;
private ContentObserver contactObserver = new ContentObserver(null) {
@@ -64,148 +67,74 @@ public class XmppConnectionService extends Service {
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(LOGTAG, "contact list has changed");
- mergePhoneContactsWithRoster();
+ mergePhoneContactsWithRoster(null);
}
};
+ private XmppConnectionService service = this;
+
private final IBinder mBinder = new XmppConnectionBinder();
private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
@Override
public void onMessagePacketReceived(Account account,
MessagePacket packet) {
- if ((packet.getType() == MessagePacket.TYPE_CHAT)
- || (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
- boolean notify = true;
- boolean runOtrCheck = false;
- int status = Message.STATUS_RECIEVED;
- int encryption = Message.ENCRYPTION_NONE;
- String body;
- String fullJid;
- if (!packet.hasChild("body")) {
- Element forwarded;
- if (packet.hasChild("received")) {
- forwarded = packet.findChild("received").findChild(
- "forwarded");
- } else if (packet.hasChild("sent")) {
- forwarded = packet.findChild("sent").findChild(
- "forwarded");
- status = Message.STATUS_SEND;
- notify = false;
- } else {
- return; // massage has no body and is not carbon. just
- // skip
- }
- if (forwarded != null) {
- Element message = forwarded.findChild("message");
- if ((message == null) || (!message.hasChild("body")))
- return; // either malformed or boring
- if (status == Message.STATUS_RECIEVED) {
- fullJid = message.getAttribute("from");
- } else {
- fullJid = message.getAttribute("to");
- }
- body = message.findChild("body").getContent();
- } else {
- return; // packet malformed. has no forwarded element
- }
- } else {
- fullJid = packet.getFrom();
- body = packet.getBody();
- runOtrCheck = true;
+ Message message = null;
+ boolean notify = false;
+ if ((packet.getType() == MessagePacket.TYPE_CHAT)) {
+ if (packet.hasChild("body")
+ && (packet.getBody().startsWith("?OTR"))) {
+ message = MessageParser.parseOtrChat(packet, account,
+ service);
+ notify = true;
+ } else if (packet.hasChild("body")) {
+ message = MessageParser.parsePlainTextChat(packet, account,
+ service);
+ notify = true;
+ } else if (packet.hasChild("received")
+ || (packet.hasChild("sent"))) {
+ message = MessageParser.parseCarbonMessage(packet, account,
+ service);
}
- Conversation conversation = null;
- String[] fromParts = fullJid.split("/");
- String jid = fromParts[0];
- boolean muc = (packet.getType() == MessagePacket.TYPE_GROUPCHAT);
- String counterPart = null;
- conversation = findOrCreateConversation(account, jid, muc);
- if (muc) {
- if ((fromParts.length == 1) || (packet.hasChild("subject"))) {
- return;
- }
- counterPart = fromParts[1];
- if (counterPart.equals(account.getUsername())) {
- status = Message.STATUS_SEND;
- notify = false;
- }
- } else {
- counterPart = fullJid;
- if ((runOtrCheck) && body.startsWith("?OTR")) {
- if (!conversation.hasValidOtrSession()) {
- conversation.startOtrSession(
- getApplicationContext(), fromParts[1]);
- }
- try {
- Session otrSession = conversation.getOtrSession();
- SessionStatus before = otrSession
- .getSessionStatus();
- body = otrSession.transformReceiving(body);
- SessionStatus after = otrSession.getSessionStatus();
- if ((before != after)
- && (after == SessionStatus.ENCRYPTED)) {
- Log.d(LOGTAG, "otr session etablished");
- List<Message> messages = conversation
- .getMessages();
- for (int i = 0; i < messages.size(); ++i) {
- Message msg = messages.get(i);
- if ((msg.getStatus() == Message.STATUS_UNSEND)
- && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
- MessagePacket outPacket = prepareMessagePacket(
- account, msg, otrSession);
- msg.setStatus(Message.STATUS_SEND);
- databaseBackend.updateMessage(msg);
- account.getXmppConnection()
- .sendMessagePacket(outPacket);
- }
- }
- if (convChangedListener!=null) {
- convChangedListener.onConversationListChanged();
- }
- } else if ((before != after) && (after == SessionStatus.FINISHED)) {
- conversation.resetOtrSession();
- Log.d(LOGTAG,"otr session stoped");
- }
- } catch (Exception e) {
- Log.d(LOGTAG, "error receiving otr. resetting");
- conversation.resetOtrSession();
- return;
- }
- if (body == null) {
- return;
- }
- encryption = Message.ENCRYPTION_OTR;
- }
+
+ } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
+ message = MessageParser
+ .parseGroupchat(packet, account, service);
+ if (message != null) {
+ notify = (message.getStatus() == Message.STATUS_RECIEVED);
}
- Message message = new Message(conversation, counterPart, body,
- encryption, status);
- if (packet.hasChild("delay")) {
- try {
- String stamp = packet.findChild("delay").getAttribute(
- "stamp");
- stamp = stamp.replace("Z", "+0000");
- Date date = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ssZ").parse(stamp);
- message.setTime(date.getTime());
- } catch (ParseException e) {
- Log.d(LOGTAG,
- "error trying to parse date" + e.getMessage());
- }
+ } else {
+ Log.d(LOGTAG, "unparsed message " + packet.toString());
+ }
+ if (message == null) {
+ return;
+ }
+ if (packet.hasChild("delay")) {
+ try {
+ String stamp = packet.findChild("delay").getAttribute(
+ "stamp");
+ stamp = stamp.replace("Z", "+0000");
+ Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
+ .parse(stamp);
+ message.setTime(date.getTime());
+ } catch (ParseException e) {
+ Log.d(LOGTAG, "error trying to parse date" + e.getMessage());
}
+ }
+ if (notify) {
+ message.markUnread();
+ }
+ Conversation conversation = message.getConversation();
+ conversation.getMessages().add(message);
+ databaseBackend.createMessage(message);
+ if (convChangedListener != null) {
+ convChangedListener.onConversationListChanged();
+ } else {
if (notify) {
- message.markUnread();
- }
- conversation.getMessages().add(message);
- databaseBackend.createMessage(message);
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
- } else {
- if (notify) {
- NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- mNotificationManager.notify(2342, UIHelper
- .getUnreadMessageNotification(
- getApplicationContext(), conversation));
- }
+ NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationManager.notify(2342, UIHelper
+ .getUnreadMessageNotification(
+ getApplicationContext(), conversation));
}
}
}
@@ -271,11 +200,54 @@ public class XmppConnectionService extends Service {
replaceContactInConversation(contact);
}
};
-
+
+ private OnIqPacketReceived unknownIqListener = new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.hasChild("query")) {
+ Element query = packet.findChild("query");
+ String xmlns = query.getAttribute("xmlns");
+ if ((xmlns != null) && (xmlns.equals("jabber:iq:roster"))) {
+ processRosterItems(account, query);
+ mergePhoneContactsWithRoster(null);
+ }
+ }
+ }
+ };
+
+ private void processRosterItems(Account account, Element elements) {
+ for (Element item : elements.getChildren()) {
+ if (item.getName().equals("item")) {
+ String jid = item.getAttribute("jid");
+ String subscription = item.getAttribute("subscription");
+ Contact contact = databaseBackend.findContact(account, jid);
+ if (contact == null) {
+ String name = item.getAttribute("name");
+ if (name == null) {
+ name = jid.split("@")[0];
+ }
+ contact = new Contact(account, name, jid, null);
+ contact.setSubscription(subscription);
+ databaseBackend.createContact(contact);
+ } else {
+ if (subscription.equals("remove")) {
+ databaseBackend.deleteContact(contact);
+ } else {
+ contact.setSubscription(subscription);
+ databaseBackend.updateContact(contact);
+ replaceContactInConversation(contact);
+ }
+ }
+ }
+ }
+ }
+
private void replaceContactInConversation(Contact contact) {
List<Conversation> conversations = getConversations();
- for(int i = 0; i < conversations.size(); ++i) {
- if (conversations.get(i).getContact().equals(contact)) {
+ for (int i = 0; i < conversations.size(); ++i) {
+ if ((conversations.get(i).getContact() != null)
+ && (conversations.get(i).getContact().equals(contact))) {
conversations.get(i).setContact(contact);
break;
}
@@ -325,11 +297,13 @@ public class XmppConnectionService extends Service {
connection.setOnMessagePacketReceivedListener(this.messageListener);
connection.setOnStatusChangedListener(this.statusListener);
connection.setOnPresencePacketReceivedListener(this.presenceListener);
+ connection
+ .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener);
Thread thread = new Thread(connection);
thread.start();
return connection;
}
-
+
public void sendMessage(Account account, Message message, String presence) {
Conversation conv = message.getConversation();
boolean saveInDb = false;
@@ -338,10 +312,11 @@ public class XmppConnectionService extends Service {
MessagePacket packet;
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) {
- //starting otr session. messages will be send later
+ // starting otr session. messages will be send later
conv.startOtrSession(getApplicationContext(), presence);
- } else if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED){
- //otr session aleary exists, creating message packet accordingly
+ } else if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
+ // otr session aleary exists, creating message packet
+ // accordingly
packet = prepareMessagePacket(account, message,
conv.getOtrSession());
account.getXmppConnection().sendMessagePacket(packet);
@@ -356,7 +331,7 @@ public class XmppConnectionService extends Service {
saveInDb = true;
addToConversation = true;
}
-
+
packet = prepareMessagePacket(account, message, null);
account.getXmppConnection().sendMessagePacket(packet);
}
@@ -398,8 +373,8 @@ public class XmppConnectionService extends Service {
}
}
- private MessagePacket prepareMessagePacket(Account account,
- Message message, Session otrSession) {
+ public MessagePacket prepareMessagePacket(Account account, Message message,
+ Session otrSession) {
MessagePacket packet = new MessagePacket();
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
packet.setType(MessagePacket.TYPE_CHAT);
@@ -416,7 +391,8 @@ public class XmppConnectionService extends Service {
Element privateMarker = new Element("private");
privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
packet.addChild(privateMarker);
- packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID());
+ packet.setTo(otrSession.getSessionID().getAccountID() + "/"
+ + otrSession.getSessionID().getUserID());
packet.setFrom(account.getFullJid());
} else {
packet.setBody(message.getBody());
@@ -445,81 +421,65 @@ public class XmppConnectionService extends Service {
public void updateRoster(final Account account,
final OnRosterFetchedListener listener) {
-
- PhoneHelper.loadPhoneContacts(this,
- new OnPhoneContactsLoadedListener() {
+ IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
+ Element query = new Element("query");
+ query.setAttribute("xmlns", "jabber:iq:roster");
+ query.setAttribute("ver", account.getRosterVersion());
+ iqPacket.addChild(query);
+ account.getXmppConnection().sendIqPacket(iqPacket,
+ new OnIqPacketReceived() {
@Override
- public void onPhoneContactsLoaded(
- final Hashtable<String, Bundle> phoneContacts) {
- IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
- Element query = new Element("query");
- query.setAttribute("xmlns", "jabber:iq:roster");
- query.setAttribute("ver", "");
- iqPacket.addChild(query);
- account.getXmppConnection().sendIqPacket(iqPacket,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(
- Account account, IqPacket packet) {
- List<Contact> contacts = new ArrayList<Contact>();
- Element roster = packet
- .findChild("query");
- if (roster != null) {
- for (Element item : roster
- .getChildren()) {
- Contact contact;
- String name = item
- .getAttribute("name");
- String jid = item
- .getAttribute("jid");
- if (phoneContacts
- .containsKey(jid)) {
- Bundle phoneContact = phoneContacts
- .get(jid);
- String systemAccount = phoneContact
- .getInt("phoneid")
- + "#"
- + phoneContact
- .getString("lookup");
- contact = new Contact(
- account,
- phoneContact
- .getString("displayname"),
- jid,
- phoneContact
- .getString("photouri"));
- contact.setSystemAccount(systemAccount);
- } else {
- if (name == null) {
- name = jid.split("@")[0];
- }
- contact = new Contact(
- account, name, jid,
- null);
-
- }
- contact.setAccount(account);
- contact.setSubscription(item
- .getAttribute("subscription"));
- contacts.add(contact);
- }
- databaseBackend
- .mergeContacts(contacts);
- if (listener != null) {
- listener.onRosterFetched(contacts);
- }
- }
+ public void onIqPacketReceived(final Account account,
+ IqPacket packet) {
+ Element roster = packet.findChild("query");
+ if (roster != null) {
+ String version = roster.getAttribute("ver");
+ processRosterItems(account, roster);
+ if (version!=null) {
+ account.setRosterVersion(version);
+ databaseBackend.updateAccount(account);
+ } else {
+ StringBuilder mWhere = new StringBuilder();
+ mWhere.append("jid NOT IN(");
+ List<Element> items = roster.getChildren();
+ for(int i = 0; i < items.size(); ++i) {
+ mWhere.append("\"");
+ mWhere.append(DatabaseUtils.sqlEscapeString(items.get(i).getAttribute("jid")));
+ if (i != items.size() - 1) {
+ mWhere.append("\",");
+ } else {
+ mWhere.append("\"");
}
- });
-
+ }
+ mWhere.append(") and accountUuid = \"");
+ mWhere.append(account.getUuid());
+ mWhere.append("\"");
+ List<Contact> contactsToDelete = databaseBackend.getContats(mWhere.toString());
+ for(Contact contact : contactsToDelete) {
+ databaseBackend.deleteContact(contact);
+ }
+ }
+ mergePhoneContactsWithRoster(new OnPhoneContactsMerged() {
+
+ @Override
+ public void phoneContactsMerged() {
+ if (listener != null) {
+ getRoster(account, listener);
+ }
+ }
+ });
+ } else {
+ if (listener != null) {
+ getRoster(account, listener);
+ }
+ }
}
});
}
- public void mergePhoneContactsWithRoster() {
- PhoneHelper.loadPhoneContacts(this,
+ public void mergePhoneContactsWithRoster(final OnPhoneContactsMerged listener) {
+ PhoneHelper.loadPhoneContacts(getApplicationContext(),
new OnPhoneContactsLoadedListener() {
@Override
public void onPhoneContactsLoaded(
@@ -550,6 +510,9 @@ public class XmppConnectionService extends Service {
}
}
}
+ if (listener!=null) {
+ listener.phoneContactsMerged();
+ }
}
});
}
@@ -606,7 +569,8 @@ public class XmppConnectionService extends Service {
conversation.setMode(Conversation.MODE_SINGLE);
}
this.databaseBackend.updateConversation(conversation);
- conversation.setContact(findContact(account, conversation.getContactJid()));
+ conversation.setContact(findContact(account,
+ conversation.getContactJid()));
} else {
String conversationName;
Contact contact = findContact(account, jid);
@@ -728,8 +692,8 @@ public class XmppConnectionService extends Service {
if (conversation.getMessages().size() != 0) {
Element history = new Element("history");
long lastMsgTime = conversation.getLatestMessage().getTimeSent();
- long diff = (System.currentTimeMillis() - lastMsgTime) / 1000;
- history.setAttribute("seconds",diff+"");
+ long diff = (System.currentTimeMillis() - lastMsgTime) / 1000 - 1;
+ history.setAttribute("seconds", diff + "");
x.addChild(history);
}
packet.addChild(x);
diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java
index 9620c16a..8be44f49 100644
--- a/src/de/gultsch/chat/ui/ConversationFragment.java
+++ b/src/de/gultsch/chat/ui/ConversationFragment.java
@@ -1,6 +1,9 @@
package de.gultsch.chat.ui;
+import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
@@ -19,13 +22,17 @@ import android.app.AlertDialog;
import android.app.Fragment;
import android.content.DialogInterface;
import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
@@ -43,6 +50,7 @@ public class ConversationFragment extends Fragment {
protected List<Message> messageList = new ArrayList<Message>();
protected ArrayAdapter<Message> messageListAdapter;
protected Contact contact;
+ protected BitmapCache mBitmapCache = new BitmapCache();
private EditText chatMsg;
@@ -104,13 +112,24 @@ public class ConversationFragment extends Fragment {
boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
"show_phone_selfcontact_picture", true);
- final Uri selfiUri;
+ Bitmap self;
+
if (showPhoneSelfContactPicture) {
- selfiUri = PhoneHelper.getSefliUri(getActivity());
+ Uri selfiUri = PhoneHelper.getSefliUri(getActivity());
+ try {
+ self = BitmapFactory.decodeStream(getActivity()
+ .getContentResolver().openInputStream(selfiUri));
+ } catch (FileNotFoundException e) {
+ self = UIHelper.getUnknownContactPicture(conversation
+ .getAccount().getJid(), 200);
+ }
} else {
- selfiUri = null;
+ self = UIHelper.getUnknownContactPicture(conversation.getAccount()
+ .getJid(), 200);
}
+ final Bitmap selfBitmap = self;
+
messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
.getApplicationContext(), R.layout.message_sent,
this.messageList) {
@@ -136,68 +155,73 @@ public class ConversationFragment extends Fragment {
public View getView(int position, View view, ViewGroup parent) {
Message item = getItem(position);
int type = getItemViewType(position);
+ ViewHolder viewHolder;
if (view == null) {
switch (type) {
case SENT:
+ viewHolder = new ViewHolder();
view = (View) inflater.inflate(R.layout.message_sent,
null);
+ viewHolder.imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.messageBody = (TextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ view.setTag(viewHolder);
break;
case RECIEVED:
+ viewHolder = new ViewHolder();
view = (View) inflater.inflate(
R.layout.message_recieved, null);
+ viewHolder.imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.messageBody = (TextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ view.setTag(viewHolder);
+ break;
+ default:
+ viewHolder = null;
break;
}
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
}
- ImageView imageView = (ImageView) view
- .findViewById(R.id.message_photo);
if (type == RECIEVED) {
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
Uri uri = item.getConversation().getProfilePhotoUri();
if (uri != null) {
- imageView.setImageURI(uri);
+ viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(), uri));
} else {
- imageView.setImageBitmap(UIHelper
- .getUnknownContactPicture(item
- .getConversation().getName(), 200));
+ viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(),null));
}
} else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
if (item.getCounterpart() != null) {
- imageView.setImageBitmap(UIHelper
- .getUnknownContactPicture(
- item.getCounterpart(), 200));
+ viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getCounterpart(),null));
} else {
- imageView.setImageBitmap(UIHelper
- .getUnknownContactPicture(item
- .getConversation().getName(), 200));
+ viewHolder.imageView.setImageBitmap(mBitmapCache.get(item.getConversation().getName(),null));
}
}
} else {
- if (selfiUri != null) {
- imageView.setImageURI(selfiUri);
- } else {
- imageView.setImageBitmap(UIHelper
- .getUnknownContactPicture(conversation
- .getAccount().getJid(), 200));
- }
+ viewHolder.imageView.setImageBitmap(selfBitmap);
}
- TextView messageBody = (TextView) view
- .findViewById(R.id.message_body);
String body = item.getBody();
if (body != null) {
- messageBody.setText(body.trim());
+ viewHolder.messageBody.setText(body.trim());
}
- TextView time = (TextView) view.findViewById(R.id.message_time);
if (item.getStatus() == Message.STATUS_UNSEND) {
- time.setTypeface(null, Typeface.ITALIC);
- time.setText("sending\u2026");
+ viewHolder.time.setTypeface(null, Typeface.ITALIC);
+ viewHolder.time.setText("sending\u2026");
} else {
- time.setTypeface(null, Typeface.NORMAL);
+ viewHolder.time.setTypeface(null, Typeface.NORMAL);
if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
|| (type != RECIEVED)) {
- time.setText(UIHelper.readableTimeDifference(item
- .getTimeSent()));
+ viewHolder.time.setText(UIHelper
+ .readableTimeDifference(item.getTimeSent()));
} else {
- time.setText(item.getCounterpart()
+ viewHolder.time.setText(item.getCounterpart()
+ " \u00B7 "
+ UIHelper.readableTimeDifference(item
.getTimeSent()));
@@ -275,24 +299,31 @@ public class ConversationFragment extends Fragment {
protected void makeFingerprintWarning(int latestEncryption) {
final LinearLayout fingerprintWarning = (LinearLayout) getView()
.findViewById(R.id.new_fingerprint);
- Set<String> knownFingerprints = conversation.getContact()
- .getOtrFingerprints();
- if ((latestEncryption == Message.ENCRYPTION_OTR)
- && (conversation.hasValidOtrSession()
- && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
- .contains(conversation.getOtrFingerprint())))) {
- fingerprintWarning.setVisibility(View.VISIBLE);
- TextView fingerprint = (TextView) getView().findViewById(
- R.id.otr_fingerprint);
- fingerprint.setText(conversation.getOtrFingerprint());
- fingerprintWarning.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- AlertDialog dialog = UIHelper.getVerifyFingerprintDialog((ConversationActivity) getActivity(),conversation,fingerprintWarning);
- dialog.show();
- }
- });
+ if (conversation.getContact() != null) {
+ Set<String> knownFingerprints = conversation.getContact()
+ .getOtrFingerprints();
+ if ((latestEncryption == Message.ENCRYPTION_OTR)
+ && (conversation.hasValidOtrSession()
+ && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
+ .contains(conversation.getOtrFingerprint())))) {
+ fingerprintWarning.setVisibility(View.VISIBLE);
+ TextView fingerprint = (TextView) getView().findViewById(
+ R.id.otr_fingerprint);
+ fingerprint.setText(conversation.getOtrFingerprint());
+ fingerprintWarning.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ AlertDialog dialog = UIHelper
+ .getVerifyFingerprintDialog(
+ (ConversationActivity) getActivity(),
+ conversation, fingerprintWarning);
+ dialog.show();
+ }
+ });
+ } else {
+ fingerprintWarning.setVisibility(View.GONE);
+ }
} else {
fingerprintWarning.setVisibility(View.GONE);
}
@@ -300,11 +331,11 @@ public class ConversationFragment extends Fragment {
protected void sendPlainTextMessage(Message message) {
ConversationActivity activity = (ConversationActivity) getActivity();
- activity.xmppConnectionService.sendMessage(conversation.getAccount(), message,
- null);
+ activity.xmppConnectionService.sendMessage(conversation.getAccount(),
+ message, null);
chatMsg.setText("");
}
-
+
protected void sendOtrMessage(final Message message) {
ConversationActivity activity = (ConversationActivity) getActivity();
final XmppConnectionService xmppService = activity.xmppConnectionService;
@@ -313,9 +344,13 @@ public class ConversationFragment extends Fragment {
conversation.getAccount(), message, null);
chatMsg.setText("");
} else {
- Hashtable<String, Integer> presences = conversation
- .getContact().getPresences();
- if (presences.size() == 0) {
+ Hashtable<String, Integer> presences;
+ if (conversation.getContact() != null) {
+ presences = conversation.getContact().getPresences();
+ } else {
+ presences = null;
+ }
+ if ((presences != null) && (presences.size() == 0)) {
AlertDialog.Builder builder = new AlertDialog.Builder(
getActivity());
builder.setTitle("Contact is offline");
@@ -330,16 +365,15 @@ public class ConversationFragment extends Fragment {
conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.sendMessage(
- conversation.getAccount(),
- message, null);
+ conversation.getAccount(), message,
+ null);
chatMsg.setText("");
}
});
builder.setNegativeButton("Cancel", null);
builder.create().show();
} else if (presences.size() == 1) {
- xmppService.sendMessage(conversation.getAccount(),
- message,
+ xmppService.sendMessage(conversation.getAccount(), message,
(String) presences.keySet().toArray()[0]);
chatMsg.setText("");
} else {
@@ -348,16 +382,51 @@ public class ConversationFragment extends Fragment {
builder.setTitle("Choose Presence");
final String[] presencesArray = new String[presences.size()];
presences.keySet().toArray(presencesArray);
- builder.setItems(presencesArray, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppService.sendMessage(conversation.getAccount(), message, presencesArray[which]);
- chatMsg.setText("");
- }
- });
+ builder.setItems(presencesArray,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ xmppService.sendMessage(
+ conversation.getAccount(), message,
+ presencesArray[which]);
+ chatMsg.setText("");
+ }
+ });
builder.create().show();
}
}
}
+
+ private static class ViewHolder {
+
+ protected TextView time;
+ protected TextView messageBody;
+ protected ImageView imageView;
+
+ }
+
+ private class BitmapCache {
+ private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
+ public Bitmap get(String name, Uri uri) {
+ if (bitmaps.containsKey(name)) {
+ return bitmaps.get(name);
+ } else {
+ Bitmap bm;
+ if (uri!=null) {
+ try {
+ bm = BitmapFactory.decodeStream(getActivity()
+ .getContentResolver().openInputStream(uri));
+ } catch (FileNotFoundException e) {
+ bm = UIHelper.getUnknownContactPicture(name, 200);
+ }
+ } else {
+ bm = UIHelper.getUnknownContactPicture(name, 200);
+ }
+ bitmaps.put(name, bm);
+ return bm;
+ }
+ }
+ }
}
diff --git a/src/de/gultsch/chat/utils/MessageParser.java b/src/de/gultsch/chat/utils/MessageParser.java
new file mode 100644
index 00000000..aec492c8
--- /dev/null
+++ b/src/de/gultsch/chat/utils/MessageParser.java
@@ -0,0 +1,119 @@
+package de.gultsch.chat.utils;
+
+import java.util.List;
+
+import net.java.otr4j.session.Session;
+import net.java.otr4j.session.SessionStatus;
+import android.util.Log;
+import de.gultsch.chat.entities.Account;
+import de.gultsch.chat.entities.Conversation;
+import de.gultsch.chat.entities.Message;
+import de.gultsch.chat.services.XmppConnectionService;
+import de.gultsch.chat.xml.Element;
+import de.gultsch.chat.xmpp.MessagePacket;
+
+public class MessageParser {
+
+ protected static final String LOGTAG = "xmppService";
+
+ public static Message parsePlainTextChat(MessagePacket packet, Account account, XmppConnectionService service) {
+ String[] fromParts = packet.getFrom().split("/");
+ Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false);
+ String body = packet.getBody();
+ return new Message(conversation, packet.getFrom(), body, Message.ENCRYPTION_NONE, Message.STATUS_RECIEVED);
+ }
+
+ public static Message parseOtrChat(MessagePacket packet, Account account, XmppConnectionService service) {
+ String[] fromParts = packet.getFrom().split("/");
+ Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false);
+ String body = packet.getBody();
+ if (!conversation.hasValidOtrSession()) {
+ conversation.startOtrSession(service.getApplicationContext(), fromParts[1]);
+ }
+ try {
+ Session otrSession = conversation.getOtrSession();
+ SessionStatus before = otrSession
+ .getSessionStatus();
+ body = otrSession.transformReceiving(body);
+ SessionStatus after = otrSession.getSessionStatus();
+ if ((before != after)
+ && (after == SessionStatus.ENCRYPTED)) {
+ Log.d(LOGTAG, "otr session etablished");
+ List<Message> messages = conversation
+ .getMessages();
+ for (int i = 0; i < messages.size(); ++i) {
+ Message msg = messages.get(i);
+ if ((msg.getStatus() == Message.STATUS_UNSEND)
+ && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
+ MessagePacket outPacket = service.prepareMessagePacket(
+ account, msg, otrSession);
+ msg.setStatus(Message.STATUS_SEND);
+ service.databaseBackend.updateMessage(msg);
+ account.getXmppConnection()
+ .sendMessagePacket(outPacket);
+ }
+ }
+ if (service.convChangedListener!=null) {
+ service.convChangedListener.onConversationListChanged();
+ }
+ } else if ((before != after) && (after == SessionStatus.FINISHED)) {
+ conversation.resetOtrSession();
+ Log.d(LOGTAG,"otr session stoped");
+ }
+ } catch (Exception e) {
+ Log.d(LOGTAG, "error receiving otr. resetting");
+ conversation.resetOtrSession();
+ return null;
+ }
+ if (body == null) {
+ return null;
+ }
+ return new Message(conversation, packet.getFrom(), body, Message.ENCRYPTION_OTR,Message.STATUS_RECIEVED);
+ }
+
+ public static Message parseGroupchat(MessagePacket packet, Account account, XmppConnectionService service) {
+ int status;
+ String[] fromParts = packet.getFrom().split("/");
+ Conversation conversation = service.findOrCreateConversation(account, fromParts[0],true);
+ if ((fromParts.length == 1) || (packet.hasChild("subject"))) {
+ return null;
+ }
+ String counterPart = fromParts[1];
+ if (counterPart.equals(account.getUsername())) {
+ status = Message.STATUS_SEND;
+ } else {
+ status = Message.STATUS_RECIEVED;
+ }
+ return new Message(conversation, counterPart, packet.getBody(), Message.ENCRYPTION_NONE, status);
+ }
+
+ public static Message parseCarbonMessage(MessagePacket packet,
+ Account account, XmppConnectionService service) {
+ // TODO Auto-generated method stub
+ int status;
+ String fullJid;
+ Element forwarded;
+ if (packet.hasChild("received")) {
+ forwarded = packet.findChild("received").findChild(
+ "forwarded");
+ status = Message.STATUS_RECIEVED;
+ } else if (packet.hasChild("sent")) {
+ forwarded = packet.findChild("sent").findChild(
+ "forwarded");
+ status = Message.STATUS_SEND;
+ } else {
+ return null;
+ }
+ Element message = forwarded.findChild("message");
+ if ((message == null) || (!message.hasChild("body")))
+ return null; // either malformed or boring
+ if (status == Message.STATUS_RECIEVED) {
+ fullJid = message.getAttribute("from");
+ } else {
+ fullJid = message.getAttribute("to");
+ }
+ String[] parts = fullJid.split("/");
+ Conversation conversation = service.findOrCreateConversation(account, parts[0],false);
+ return new Message(conversation,fullJid, message.findChild("body").getContent(), Message.ENCRYPTION_NONE,status);
+ }
+}
diff --git a/src/de/gultsch/chat/utils/PhoneHelper.java b/src/de/gultsch/chat/utils/PhoneHelper.java
index 3a53c08f..14773caa 100644
--- a/src/de/gultsch/chat/utils/PhoneHelper.java
+++ b/src/de/gultsch/chat/utils/PhoneHelper.java
@@ -1,6 +1,5 @@
package de.gultsch.chat.utils;
-import java.util.ArrayList;
import java.util.Hashtable;
import android.app.Activity;
@@ -11,14 +10,19 @@ import android.content.Loader.OnLoadCompleteListener;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Looper;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Profile;
public class PhoneHelper {
public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
+ if (Looper.myLooper()==null) {
+ Looper.prepare();
+ }
+ final Looper mLooper = Looper.myLooper();
final Hashtable<String, Bundle> phoneContacts = new Hashtable<String, Bundle>();
-
+
final String[] PROJECTION = new String[] {
ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME,
@@ -31,7 +35,7 @@ public class PhoneHelper {
+ "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
+ "\")";
-
+
CursorLoader mCursorLoader = new CursorLoader(context,
ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
null);
@@ -61,6 +65,7 @@ public class PhoneHelper {
if (listener!=null) {
listener.onPhoneContactsLoaded(phoneContacts);
}
+ mLooper.quit();
}
});
mCursorLoader.startLoading();
diff --git a/src/de/gultsch/chat/xml/XmlReader.java b/src/de/gultsch/chat/xml/XmlReader.java
index e9429665..0ff2e785 100644
--- a/src/de/gultsch/chat/xml/XmlReader.java
+++ b/src/de/gultsch/chat/xml/XmlReader.java
@@ -59,6 +59,10 @@ public class XmlReader {
for(int i = 0; i < parser.getAttributeCount(); ++i) {
tag.setAttribute(parser.getAttributeName(i), parser.getAttributeValue(i));
}
+ String xmlns = parser.getNamespace();
+ if (xmlns!=null) {
+ tag.setAttribute("xmlns",xmlns);
+ }
return tag;
} else if (parser.getEventType() == XmlPullParser.END_TAG) {
Tag tag = Tag.end(parser.getName());