basic otr support
This commit is contained in:
parent
e63109215e
commit
42c4c1789a
|
@ -31,14 +31,15 @@ public final class R {
|
|||
public static final int ic_action_add_person=0x7f020002;
|
||||
public static final int ic_action_delete=0x7f020003;
|
||||
public static final int ic_action_refresh=0x7f020004;
|
||||
public static final int ic_action_send=0x7f020005;
|
||||
public static final int ic_action_send_now=0x7f020006;
|
||||
public static final int ic_action_unsecure=0x7f020007;
|
||||
public static final int ic_launcher=0x7f020008;
|
||||
public static final int ic_profile=0x7f020009;
|
||||
public static final int message_border=0x7f02000a;
|
||||
public static final int notification=0x7f02000b;
|
||||
public static final int section_header=0x7f02000c;
|
||||
public static final int ic_action_secure=0x7f020005;
|
||||
public static final int ic_action_send=0x7f020006;
|
||||
public static final int ic_action_send_now=0x7f020007;
|
||||
public static final int ic_action_unsecure=0x7f020008;
|
||||
public static final int ic_launcher=0x7f020009;
|
||||
public static final int ic_profile=0x7f02000a;
|
||||
public static final int message_border=0x7f02000b;
|
||||
public static final int notification=0x7f02000c;
|
||||
public static final int section_header=0x7f02000d;
|
||||
}
|
||||
public static final class id {
|
||||
public static final int account_confirm_password_desc=0x7f0a0019;
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 394 B |
Binary file not shown.
After Width: | Height: | Size: 317 B |
Binary file not shown.
After Width: | Height: | Size: 510 B |
Binary file not shown.
After Width: | Height: | Size: 624 B |
|
@ -0,0 +1,229 @@
|
|||
package de.gultsch.chat.crypto;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.DSAPrivateKeySpec;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import de.gultsch.chat.entities.Account;
|
||||
import de.gultsch.chat.persistance.DatabaseBackend;
|
||||
import de.gultsch.chat.xml.Element;
|
||||
import de.gultsch.chat.xmpp.MessagePacket;
|
||||
|
||||
import net.java.otr4j.OtrEngineHost;
|
||||
import net.java.otr4j.OtrException;
|
||||
import net.java.otr4j.OtrPolicy;
|
||||
import net.java.otr4j.OtrPolicyImpl;
|
||||
import net.java.otr4j.session.InstanceTag;
|
||||
import net.java.otr4j.session.SessionID;
|
||||
|
||||
public class OtrEngine implements OtrEngineHost {
|
||||
|
||||
private static final String LOGTAG = "xmppService";
|
||||
|
||||
private Account account;
|
||||
private OtrPolicy otrPolicy;
|
||||
private KeyPair keyPair;
|
||||
private Context context;
|
||||
|
||||
public OtrEngine(Context context, Account account) {
|
||||
this.account = account;
|
||||
this.otrPolicy = new OtrPolicyImpl();
|
||||
this.otrPolicy.setAllowV1(false);
|
||||
this.otrPolicy.setAllowV2(true);
|
||||
this.otrPolicy.setAllowV3(true);
|
||||
this.keyPair = loadKey(account.getKeys());
|
||||
}
|
||||
|
||||
private KeyPair loadKey(JSONObject keys) {
|
||||
if (keys == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BigInteger x = new BigInteger(keys.getString("otr_x"),16);
|
||||
BigInteger y = new BigInteger(keys.getString("otr_y"),16);
|
||||
BigInteger p = new BigInteger(keys.getString("otr_p"),16);
|
||||
BigInteger q = new BigInteger(keys.getString("otr_q"),16);
|
||||
BigInteger g = new BigInteger(keys.getString("otr_g"),16);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
|
||||
DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
|
||||
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
} catch (JSONException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InvalidKeySpecException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void saveKey() {
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
KeyFactory keyFactory;
|
||||
try {
|
||||
keyFactory = KeyFactory.getInstance("DSA");
|
||||
DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, DSAPrivateKeySpec.class);
|
||||
DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class);
|
||||
this.account.setKey("otr_x",privateKeySpec.getX().toString(16));
|
||||
this.account.setKey("otr_g",privateKeySpec.getG().toString(16));
|
||||
this.account.setKey("otr_p",privateKeySpec.getP().toString(16));
|
||||
this.account.setKey("otr_q",privateKeySpec.getQ().toString(16));
|
||||
this.account.setKey("otr_y",publicKeySpec.getY().toString(16));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidKeySpecException e) {
|
||||
e.printStackTrace();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishedSessionMessage(SessionID arg0, String arg1)
|
||||
throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFallbackMessage(SessionID arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getLocalFingerprintRaw(SessionID arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
|
||||
if (this.keyPair==null) {
|
||||
KeyPairGenerator kg;
|
||||
try {
|
||||
kg = KeyPairGenerator.getInstance("DSA");
|
||||
this.keyPair = kg.genKeyPair();
|
||||
this.saveKey();
|
||||
DatabaseBackend.getInstance(context).updateAccount(account);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(LOGTAG,"error generating key pair "+e.getMessage());
|
||||
}
|
||||
}
|
||||
return this.keyPair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplyForUnreadableMessage(SessionID arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OtrPolicy getSessionPolicy(SessionID arg0) {
|
||||
return otrPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMessage(SessionID session, String body) throws OtrException {
|
||||
MessagePacket packet = new MessagePacket();
|
||||
packet.setFrom(account.getFullJid()); //sender
|
||||
packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient
|
||||
packet.setBody(body);
|
||||
Element privateTag = new Element("private");
|
||||
privateTag.setAttribute("xmlns","urn:xmpp:carbons:2");
|
||||
packet.addChild(privateTag);
|
||||
account.getXmppConnection().sendMessagePacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageFromAnotherInstanceReceived(SessionID arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multipleInstancesDetected(SessionID arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireEncryptedMessage(SessionID arg0, String arg1)
|
||||
throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(SessionID arg0, String arg1) throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void smpAborted(SessionID arg0) throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void smpError(SessionID arg0, int arg1, boolean arg2)
|
||||
throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unencryptedMessageReceived(SessionID arg0, String arg1)
|
||||
throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unreadableMessageReceived(SessionID arg0) throws OtrException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unverify(SessionID arg0, String arg1) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(SessionID arg0, String arg1, boolean arg2) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
package de.gultsch.chat.entities;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.gultsch.chat.crypto.OtrEngine;
|
||||
import de.gultsch.chat.xmpp.XmppConnection;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.util.JsonReader;
|
||||
import android.util.Log;
|
||||
|
||||
public class Account extends AbstractEntity{
|
||||
|
@ -15,6 +22,7 @@ public class Account extends AbstractEntity{
|
|||
public static final String PASSWORD = "password";
|
||||
public static final String OPTIONS = "options";
|
||||
public static final String ROSTERVERSION = "rosterversion";
|
||||
public static final String KEYS = "keys";
|
||||
|
||||
public static final int OPTION_USETLS = 0;
|
||||
public static final int OPTION_DISABLED = 1;
|
||||
|
@ -34,23 +42,32 @@ public class Account extends AbstractEntity{
|
|||
protected String rosterVersion;
|
||||
protected String resource;
|
||||
protected int status = 0;
|
||||
protected JSONObject keys = new JSONObject();
|
||||
|
||||
protected boolean online = false;
|
||||
|
||||
transient OtrEngine otrEngine = null;
|
||||
transient XmppConnection xmppConnection = null;
|
||||
|
||||
public Account() {
|
||||
this.uuid = "0";
|
||||
}
|
||||
|
||||
public Account(String username, String server, String password) {
|
||||
this(java.util.UUID.randomUUID().toString(),username,server,password,0,null);
|
||||
this(java.util.UUID.randomUUID().toString(),username,server,password,0,null,"");
|
||||
}
|
||||
public Account(String uuid, String username, String server,String password, int options, String rosterVersion) {
|
||||
public Account(String uuid, String username, String server,String password, int options, String rosterVersion, String keys) {
|
||||
this.uuid = uuid;
|
||||
this.username = username;
|
||||
this.server = server;
|
||||
this.password = password;
|
||||
this.options = options;
|
||||
this.rosterVersion = rosterVersion;
|
||||
try {
|
||||
this.keys = new JSONObject(keys);
|
||||
} catch (JSONException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOptionSet(int option) {
|
||||
|
@ -109,6 +126,14 @@ public class Account extends AbstractEntity{
|
|||
return username+"@"+server;
|
||||
}
|
||||
|
||||
public JSONObject getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public void setKey(String keyName, String keyValue) throws JSONException {
|
||||
this.keys.put(keyName, keyValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentValues getContentValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
|
@ -117,6 +142,8 @@ public class Account extends AbstractEntity{
|
|||
values.put(SERVER, server);
|
||||
values.put(PASSWORD, password);
|
||||
values.put(OPTIONS,options);
|
||||
values.put(KEYS,this.keys.toString());
|
||||
values.put(ROSTERVERSION,rosterVersion);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -126,8 +153,28 @@ public class Account extends AbstractEntity{
|
|||
cursor.getString(cursor.getColumnIndex(SERVER)),
|
||||
cursor.getString(cursor.getColumnIndex(PASSWORD)),
|
||||
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
|
||||
cursor.getString(cursor.getColumnIndex(ROSTERVERSION))
|
||||
cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
|
||||
cursor.getString(cursor.getColumnIndex(KEYS))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public OtrEngine getOtrEngine(Context context) {
|
||||
if (otrEngine==null) {
|
||||
otrEngine = new OtrEngine(context,this);
|
||||
}
|
||||
return this.otrEngine;
|
||||
}
|
||||
|
||||
public XmppConnection getXmppConnection() {
|
||||
return this.xmppConnection;
|
||||
}
|
||||
|
||||
public void setXmppConnection(XmppConnection connection) {
|
||||
this.xmppConnection = connection;
|
||||
}
|
||||
|
||||
public String getFullJid() {
|
||||
return this.getJid()+"/"+this.resource;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,16 @@ package de.gultsch.chat.entities;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.gultsch.chat.crypto.OtrEngine;
|
||||
import de.gultsch.chat.xmpp.XmppConnection;
|
||||
|
||||
import net.java.otr4j.OtrException;
|
||||
import net.java.otr4j.session.SessionID;
|
||||
import net.java.otr4j.session.SessionImpl;
|
||||
import net.java.otr4j.session.SessionStatus;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
@ -41,6 +50,9 @@ public class Conversation extends AbstractEntity {
|
|||
private transient Account account = null;
|
||||
private transient Contact contact;
|
||||
|
||||
private transient SessionImpl otrSession;
|
||||
private transient String foreignOtrPresence;
|
||||
|
||||
public Conversation(String name, Account account,
|
||||
String contactJid, int mode) {
|
||||
this(java.util.UUID.randomUUID().toString(), name, null, account.getUuid(), contactJid, System
|
||||
|
@ -85,19 +97,13 @@ public class Conversation extends AbstractEntity {
|
|||
}
|
||||
}
|
||||
|
||||
public String getLatestMessage() {
|
||||
public Message getLatestMessage() {
|
||||
if ((this.messages == null)||(this.messages.size()==0)) {
|
||||
return null;
|
||||
Message message = new Message(this,"",Message.ENCRYPTION_NONE);
|
||||
message.setTime(0);
|
||||
return message;
|
||||
} else {
|
||||
return this.messages.get(this.messages.size() - 1).getBody();
|
||||
}
|
||||
}
|
||||
|
||||
public long getLatestMessageDate() {
|
||||
if ((this.messages == null)||(this.messages.size()==0)) {
|
||||
return this.getCreated();
|
||||
} else {
|
||||
return this.messages.get(this.messages.size() - 1).getTimeSent();
|
||||
return this.messages.get(this.messages.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,4 +204,30 @@ public class Conversation extends AbstractEntity {
|
|||
public void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public void startOtrSession(Context context, String presence) {
|
||||
Log.d("xmppService","starting otr session with "+presence);
|
||||
SessionID sessionId = new SessionID(this.getContactJid(),presence,"xmpp");
|
||||
this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine(context));
|
||||
}
|
||||
|
||||
public SessionImpl getOtrSession() {
|
||||
return this.otrSession;
|
||||
}
|
||||
|
||||
public void resetOtrSession() {
|
||||
this.otrSession = null;
|
||||
}
|
||||
|
||||
public void endOtrIfNeeded() throws OtrException {
|
||||
if (this.otrSession!=null) {
|
||||
if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
|
||||
this.otrSession.endSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasOtrSession() {
|
||||
return (this.otrSession!=null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
|
||||
+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
|
||||
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
|
||||
+ " NUMBER)");
|
||||
+ " NUMBER, "+Account.KEYS+" TEXT)");
|
||||
db.execSQL("create table " + Conversation.TABLENAME + " ("
|
||||
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
|
||||
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
|
||||
|
|
|
@ -6,6 +6,12 @@ import java.util.ArrayList;
|
|||
import java.util.Date;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.java.otr4j.OtrException;
|
||||
import net.java.otr4j.session.Session;
|
||||
import net.java.otr4j.session.SessionImpl;
|
||||
import net.java.otr4j.session.SessionStatus;
|
||||
|
||||
import de.gultsch.chat.entities.Account;
|
||||
import de.gultsch.chat.entities.Contact;
|
||||
|
@ -50,8 +56,6 @@ public class XmppConnectionService extends Service {
|
|||
private List<Account> accounts;
|
||||
private List<Conversation> conversations = null;
|
||||
|
||||
private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
|
||||
|
||||
private OnConversationListChangedListener convChangedListener = null;
|
||||
private OnAccountListChangedListener accountChangedListener = null;
|
||||
|
||||
|
@ -73,7 +77,9 @@ public class XmppConnectionService extends Service {
|
|||
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")) {
|
||||
|
@ -106,6 +112,7 @@ public class XmppConnectionService extends Service {
|
|||
} else {
|
||||
fullJid = packet.getFrom();
|
||||
body = packet.getBody();
|
||||
runOtrCheck = true;
|
||||
}
|
||||
Conversation conversation = null;
|
||||
String[] fromParts = fullJid.split("/");
|
||||
|
@ -124,9 +131,51 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
} else {
|
||||
counterPart = fullJid;
|
||||
if ((runOtrCheck) && body.startsWith("?OTR")) {
|
||||
if (!conversation.hasOtrSession()) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(LOGTAG, "error receiving otr. resetting");
|
||||
conversation.resetOtrSession();
|
||||
return;
|
||||
}
|
||||
if (body == null) {
|
||||
return;
|
||||
}
|
||||
encryption = Message.ENCRYPTION_OTR;
|
||||
}
|
||||
}
|
||||
Message message = new Message(conversation, counterPart, body,
|
||||
Message.ENCRYPTION_NONE, status);
|
||||
encryption, status);
|
||||
if (packet.hasChild("delay")) {
|
||||
try {
|
||||
String stamp = packet.findChild("delay").getAttribute(
|
||||
|
@ -216,9 +265,20 @@ public class XmppConnectionService extends Service {
|
|||
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)) {
|
||||
conversations.get(i).setContact(contact);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class XmppConnectionBinder extends Binder {
|
||||
public XmppConnectionService getService() {
|
||||
return XmppConnectionService.this;
|
||||
|
@ -228,13 +288,9 @@ public class XmppConnectionService extends Service {
|
|||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
for (Account account : accounts) {
|
||||
if (!connections.containsKey(account)) {
|
||||
if (account.getXmppConnection() == null) {
|
||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||
this.connections.put(account,
|
||||
this.createConnection(account));
|
||||
} else {
|
||||
Log.d(LOGTAG, account.getJid()
|
||||
+ ": not starting because it's disabled");
|
||||
account.setXmppConnection(this.createConnection(account));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,6 +306,16 @@ public class XmppConnectionService extends Service {
|
|||
ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
for (Account account : accounts) {
|
||||
if (account.getXmppConnection() != null) {
|
||||
disconnect(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public XmppConnection createConnection(Account account) {
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
XmppConnection connection = new XmppConnection(account, pm);
|
||||
|
@ -261,24 +327,90 @@ public class XmppConnectionService extends Service {
|
|||
return connection;
|
||||
}
|
||||
|
||||
public void sendMessage(Account account, Message message) {
|
||||
|
||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||
MessagePacket packet = prepareMessagePacket(account, message);
|
||||
connections.get(account).sendMessagePacket(packet);
|
||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||
databaseBackend.createMessage(message);
|
||||
message.getConversation().getMessages().add(message);
|
||||
if (convChangedListener!=null) {
|
||||
convChangedListener.onConversationListChanged();
|
||||
}
|
||||
}
|
||||
private void startOtrSession(Conversation conv) {
|
||||
Set<String> presences = conv.getContact().getPresences()
|
||||
.keySet();
|
||||
if (presences.size() == 0) {
|
||||
Log.d(LOGTAG, "counter part isnt online. cant use otr");
|
||||
return;
|
||||
} else if (presences.size() == 1) {
|
||||
conv.startOtrSession(getApplicationContext(),
|
||||
(String) presences.toArray()[0]);
|
||||
try {
|
||||
conv.getOtrSession().startSession();
|
||||
} catch (OtrException e) {
|
||||
Log.d(LOGTAG, "couldnt actually start");
|
||||
}
|
||||
} else {
|
||||
message.getConversation().getMessages().add(message);
|
||||
String latestCounterpartPresence = null;
|
||||
List<Message> messages = conv.getMessages();
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
if (messages.get(i).getStatus() == Message.STATUS_RECIEVED) {
|
||||
String[] parts = messages.get(i).getCounterpart()
|
||||
.split("/");
|
||||
if (parts.length == 2) {
|
||||
latestCounterpartPresence = parts[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (presences.contains(latestCounterpartPresence)) {
|
||||
conv.startOtrSession(getApplicationContext(),
|
||||
latestCounterpartPresence);
|
||||
try {
|
||||
conv.getOtrSession().startSession();
|
||||
} catch (OtrException e) {
|
||||
// TODO Auto-generated catch block
|
||||
Log.d(LOGTAG, "couldnt actually start");
|
||||
}
|
||||
} else {
|
||||
Log.d(LOGTAG,
|
||||
"could not decide where to send otr connection to");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(Account account, Message message) {
|
||||
Conversation conv = message.getConversation();
|
||||
boolean saveInDb = false;
|
||||
boolean addToConversation = false;
|
||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||
MessagePacket packet;
|
||||
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
||||
if (!conv.hasOtrSession()) {
|
||||
//starting otr session. messages will be send later
|
||||
startOtrSession(conv);
|
||||
} else {
|
||||
//otr session aleary exists, creating message packet accordingly
|
||||
packet = prepareMessagePacket(account, message,
|
||||
conv.getOtrSession());
|
||||
account.getXmppConnection().sendMessagePacket(packet);
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
}
|
||||
saveInDb = true;
|
||||
addToConversation = true;
|
||||
} else {
|
||||
// don't encrypt
|
||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
saveInDb = true;
|
||||
addToConversation = true;
|
||||
}
|
||||
|
||||
packet = prepareMessagePacket(account, message, null);
|
||||
account.getXmppConnection().sendMessagePacket(packet);
|
||||
}
|
||||
} else {
|
||||
// account is offline
|
||||
saveInDb = true;
|
||||
addToConversation = true;
|
||||
|
||||
}
|
||||
if (saveInDb) {
|
||||
databaseBackend.createMessage(message);
|
||||
}
|
||||
if (addToConversation) {
|
||||
conv.getMessages().add(message);
|
||||
if (convChangedListener != null) {
|
||||
convChangedListener.onConversationListChanged();
|
||||
}
|
||||
|
@ -289,12 +421,11 @@ public class XmppConnectionService extends Service {
|
|||
private void sendUnsendMessages(Conversation conversation) {
|
||||
for (int i = 0; i < conversation.getMessages().size(); ++i) {
|
||||
if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
|
||||
Message message = conversation.getMessages()
|
||||
.get(i);
|
||||
Message message = conversation.getMessages().get(i);
|
||||
MessagePacket packet = prepareMessagePacket(
|
||||
conversation.getAccount(),message);
|
||||
connections.get(conversation.getAccount()).sendMessagePacket(
|
||||
packet);
|
||||
conversation.getAccount(), message, null);
|
||||
conversation.getAccount().getXmppConnection()
|
||||
.sendMessagePacket(packet);
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
||||
databaseBackend.updateMessage(message);
|
||||
|
@ -307,16 +438,37 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private MessagePacket prepareMessagePacket(Account account, Message message) {
|
||||
private MessagePacket prepareMessagePacket(Account account,
|
||||
Message message, Session otrSession) {
|
||||
MessagePacket packet = new MessagePacket();
|
||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||
packet.setType(MessagePacket.TYPE_CHAT);
|
||||
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
||||
if (otrSession != null) {
|
||||
try {
|
||||
packet.setBody(otrSession.transformSending(message
|
||||
.getBody()));
|
||||
} catch (OtrException e) {
|
||||
Log.d(LOGTAG,
|
||||
account.getJid()
|
||||
+ ": could not encrypt message to "
|
||||
+ message.getCounterpart());
|
||||
}
|
||||
Element privateMarker = new Element("private");
|
||||
privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
|
||||
packet.addChild(privateMarker);
|
||||
packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID());
|
||||
packet.setFrom(account.getFullJid());
|
||||
} else {
|
||||
packet.setBody(message.getBody());
|
||||
packet.setTo(message.getCounterpart());
|
||||
packet.setFrom(account.getJid());
|
||||
}
|
||||
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
||||
packet.setBody(message.getBody());
|
||||
packet.setTo(message.getCounterpart());
|
||||
packet.setFrom(account.getJid());
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
@ -345,7 +497,7 @@ public class XmppConnectionService extends Service {
|
|||
query.setAttribute("xmlns", "jabber:iq:roster");
|
||||
query.setAttribute("ver", "");
|
||||
iqPacket.addChild(query);
|
||||
connections.get(account).sendIqPacket(iqPacket,
|
||||
account.getXmppConnection().sendIqPacket(iqPacket,
|
||||
new OnIqPacketReceived() {
|
||||
|
||||
@Override
|
||||
|
@ -488,7 +640,7 @@ public class XmppConnectionService extends Service {
|
|||
if (muc) {
|
||||
conversation.setMode(Conversation.MODE_MULTI);
|
||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||
joinMuc(account, conversation);
|
||||
joinMuc(conversation);
|
||||
}
|
||||
} else {
|
||||
conversation.setMode(Conversation.MODE_SINGLE);
|
||||
|
@ -506,7 +658,7 @@ public class XmppConnectionService extends Service {
|
|||
conversation = new Conversation(conversationName, account, jid,
|
||||
Conversation.MODE_MULTI);
|
||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||
joinMuc(account, conversation);
|
||||
joinMuc(conversation);
|
||||
}
|
||||
} else {
|
||||
conversation = new Conversation(conversationName, account, jid,
|
||||
|
@ -523,6 +675,17 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void archiveConversation(Conversation conversation) {
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
leaveMuc(conversation);
|
||||
} else {
|
||||
try {
|
||||
conversation.endOtrIfNeeded();
|
||||
} catch (OtrException e) {
|
||||
Log.d(LOGTAG,
|
||||
"error ending otr session for "
|
||||
+ conversation.getName());
|
||||
}
|
||||
}
|
||||
this.databaseBackend.updateConversation(conversation);
|
||||
this.conversations.remove(conversation);
|
||||
if (this.convChangedListener != null) {
|
||||
|
@ -537,23 +700,18 @@ public class XmppConnectionService extends Service {
|
|||
public void createAccount(Account account) {
|
||||
databaseBackend.createAccount(account);
|
||||
this.accounts.add(account);
|
||||
this.connections.put(account, this.createConnection(account));
|
||||
account.setXmppConnection(this.createConnection(account));
|
||||
if (accountChangedListener != null)
|
||||
accountChangedListener.onAccountListChangedListener();
|
||||
}
|
||||
|
||||
public void updateAccount(Account account) {
|
||||
databaseBackend.updateAccount(account);
|
||||
XmppConnection connection = this.connections.get(account);
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
this.connections.remove(account);
|
||||
if (account.getXmppConnection() != null) {
|
||||
disconnect(account);
|
||||
}
|
||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||
this.connections.put(account, this.createConnection(account));
|
||||
} else {
|
||||
Log.d(LOGTAG, account.getJid()
|
||||
+ ": not starting because it's disabled");
|
||||
account.setXmppConnection(this.createConnection(account));
|
||||
}
|
||||
if (accountChangedListener != null)
|
||||
accountChangedListener.onAccountListChangedListener();
|
||||
|
@ -561,10 +719,8 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
public void deleteAccount(Account account) {
|
||||
Log.d(LOGTAG, "called delete account");
|
||||
if (this.connections.containsKey(account)) {
|
||||
Log.d(LOGTAG, "found connection. disconnecting");
|
||||
this.connections.get(account).disconnect();
|
||||
this.connections.remove(account);
|
||||
if (account.getXmppConnection() != null) {
|
||||
this.disconnect(account);
|
||||
}
|
||||
databaseBackend.deleteAccount(account);
|
||||
this.accounts.remove(account);
|
||||
|
@ -596,33 +752,56 @@ public class XmppConnectionService extends Service {
|
|||
Conversation conversation = conversations.get(i);
|
||||
if ((conversation.getMode() == Conversation.MODE_MULTI)
|
||||
&& (conversation.getAccount() == account)) {
|
||||
joinMuc(account, conversation);
|
||||
joinMuc(conversation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void joinMuc(Account account, Conversation conversation) {
|
||||
public void joinMuc(Conversation conversation) {
|
||||
String muc = conversation.getContactJid();
|
||||
PresencePacket packet = new PresencePacket();
|
||||
packet.setAttribute("to", muc + "/" + account.getUsername());
|
||||
packet.setAttribute("to", muc + "/"
|
||||
+ conversation.getAccount().getUsername());
|
||||
Element x = new Element("x");
|
||||
x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
|
||||
if (conversation.getMessages().size() != 0) {
|
||||
Element history = new Element("history");
|
||||
history.setAttribute(
|
||||
"seconds",
|
||||
history.setAttribute("seconds",
|
||||
(System.currentTimeMillis() - conversation
|
||||
.getLatestMessageDate()) / 1000 + "");
|
||||
.getLatestMessage().getTimeSent() / 1000) + "");
|
||||
x.addChild(history);
|
||||
}
|
||||
packet.addChild(x);
|
||||
connections.get(conversation.getAccount()).sendPresencePacket(packet);
|
||||
conversation.getAccount().getXmppConnection()
|
||||
.sendPresencePacket(packet);
|
||||
}
|
||||
|
||||
public void disconnectMultiModeConversations() {
|
||||
public void leaveMuc(Conversation conversation) {
|
||||
|
||||
}
|
||||
|
||||
public void disconnect(Account account) {
|
||||
List<Conversation> conversations = getConversations();
|
||||
for (int i = 0; i < conversations.size(); i++) {
|
||||
Conversation conversation = conversations.get(i);
|
||||
if (conversation.getAccount() == account) {
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
leaveMuc(conversation);
|
||||
} else {
|
||||
try {
|
||||
conversation.endOtrIfNeeded();
|
||||
} catch (OtrException e) {
|
||||
Log.d(LOGTAG, "error ending otr session for "
|
||||
+ conversation.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
account.getXmppConnection().disconnect();
|
||||
Log.d(LOGTAG, "disconnected account: " + account.getJid());
|
||||
account.setXmppConnection(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
|
|
|
@ -9,6 +9,7 @@ import de.gultsch.chat.R;
|
|||
import de.gultsch.chat.R.id;
|
||||
import de.gultsch.chat.entities.Contact;
|
||||
import de.gultsch.chat.entities.Conversation;
|
||||
import de.gultsch.chat.entities.Message;
|
||||
import de.gultsch.chat.utils.UIHelper;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
@ -114,7 +115,7 @@ public class ConversationActivity extends XmppActivity {
|
|||
Collections.sort(this.conversationList, new Comparator<Conversation>() {
|
||||
@Override
|
||||
public int compare(Conversation lhs, Conversation rhs) {
|
||||
return (int) (rhs.getLatestMessageDate() - lhs.getLatestMessageDate());
|
||||
return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -143,7 +144,7 @@ public class ConversationActivity extends XmppActivity {
|
|||
TextView convName = (TextView) view.findViewById(R.id.conversation_name);
|
||||
convName.setText(conv.getName());
|
||||
TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
|
||||
convLastMsg.setText(conv.getLatestMessage());
|
||||
convLastMsg.setText(conv.getLatestMessage().getBody());
|
||||
|
||||
if(!conv.isRead()) {
|
||||
convName.setTypeface(null,Typeface.BOLD);
|
||||
|
@ -154,7 +155,7 @@ public class ConversationActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
((TextView) view.findViewById(R.id.conversation_lastupdate))
|
||||
.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessageDate()));
|
||||
.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent()));
|
||||
|
||||
Uri profilePhoto = getItem(position).getProfilePhotoUri();
|
||||
ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
|
||||
|
@ -238,18 +239,23 @@ public class ConversationActivity extends XmppActivity {
|
|||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.conversations, menu);
|
||||
MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
|
||||
|
||||
if (spl.isOpen()) {
|
||||
((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false);
|
||||
((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
|
||||
((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
|
||||
menuSecure.setVisible(false);
|
||||
} else {
|
||||
((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
|
||||
if (this.getSelectedConversation()!=null) {
|
||||
if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
|
||||
((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
|
||||
((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
|
||||
menuSecure.setVisible(false);
|
||||
((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference");
|
||||
} else {
|
||||
if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) {
|
||||
menuSecure.setIcon(R.drawable.ic_action_secure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ 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;
|
||||
|
@ -36,38 +35,30 @@ public class ConversationFragment extends Fragment {
|
|||
protected ArrayAdapter<Message> messageListAdapter;
|
||||
protected Contact contact;
|
||||
|
||||
private EditText chatMsg;
|
||||
private int nextMessageEncryption = Message.ENCRYPTION_NONE;
|
||||
|
||||
@Override
|
||||
public View onCreateView(final LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
this.inflater = inflater;
|
||||
|
||||
|
||||
final View view = inflater.inflate(R.layout.fragment_conversation,
|
||||
container, false);
|
||||
chatMsg = (EditText) view.findViewById(R.id.textinput);
|
||||
((ImageButton) view.findViewById(R.id.textSendButton))
|
||||
.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ConversationActivity activity = (ConversationActivity) getActivity();
|
||||
EditText chatMsg = (EditText) view
|
||||
.findViewById(R.id.textinput);
|
||||
if (chatMsg.getText().length() < 1)
|
||||
return;
|
||||
Message message = new Message(conversation, chatMsg
|
||||
.getText().toString(), Message.ENCRYPTION_NONE);
|
||||
.getText().toString(), nextMessageEncryption);
|
||||
activity.xmppConnectionService.sendMessage(conversation.getAccount(),message);
|
||||
chatMsg.setText("");
|
||||
|
||||
/*if (conversation.getMode()==Conversation.MODE_SINGLE) {
|
||||
conversation.getMessages().add(message);
|
||||
messageList.add(message);
|
||||
}*/
|
||||
|
||||
//activity.updateConversationList();
|
||||
|
||||
//messagesView.setSelection(messageList.size() - 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -213,6 +204,22 @@ public class ConversationFragment extends Fragment {
|
|||
this.messageList.clear();
|
||||
this.messageList.addAll(this.conversation.getMessages());
|
||||
this.messageListAdapter.notifyDataSetChanged();
|
||||
if (messageList.size()>=1) {
|
||||
nextMessageEncryption = this.conversation.getLatestMessage().getEncryption();
|
||||
}
|
||||
getActivity().invalidateOptionsMenu();
|
||||
switch (nextMessageEncryption) {
|
||||
case Message.ENCRYPTION_NONE:
|
||||
chatMsg.setHint("Send plain text message");
|
||||
break;
|
||||
case Message.ENCRYPTION_OTR:
|
||||
chatMsg.setHint("Send OTR encrypted message");
|
||||
break;
|
||||
case Message.ENCRYPTION_PGP:
|
||||
chatMsg.setHint("Send openPGP encryted messeage");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int size = this.messageList.size();
|
||||
if (size >= 1)
|
||||
messagesView.setSelection(size - 1);
|
||||
|
|
|
@ -92,7 +92,7 @@ public class UIHelper {
|
|||
.getName(), (int) res
|
||||
.getDimension(android.R.dimen.notification_large_icon_width)));
|
||||
mBuilder.setContentTitle(conversation.getName());
|
||||
mBuilder.setTicker(conversation.getLatestMessage().trim());
|
||||
mBuilder.setTicker(conversation.getLatestMessage().getBody().trim());
|
||||
StringBuilder bigText = new StringBuilder();
|
||||
List<Message> messages = conversation.getMessages();
|
||||
String firstLine = "";
|
||||
|
|
|
@ -3,12 +3,7 @@ package de.gultsch.chat.xml;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ public class MessagePacket extends Element {
|
|||
}
|
||||
|
||||
public void setBody(String text) {
|
||||
this.children.remove(findChild("body"));
|
||||
Element body = new Element("body");
|
||||
body.setContent(text);
|
||||
this.children.add(body);
|
||||
|
|
|
@ -411,6 +411,7 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
public void sendMessagePacket(MessagePacket packet) {
|
||||
Log.d(LOGTAG,"sending message packet "+packet.toString());
|
||||
tagWriter.writeElement(packet);
|
||||
}
|
||||
|
||||
|
@ -440,6 +441,6 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
public void disconnect() {
|
||||
shouldConnect = false;
|
||||
tagWriter.writeTag(Tag.end("stream"));
|
||||
tagWriter.writeTag(Tag.end("stream:stream"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue