1package de.gultsch.chat.services;
2
3import java.text.ParseException;
4import java.text.SimpleDateFormat;
5import java.util.ArrayList;
6import java.util.Date;
7import java.util.Hashtable;
8import java.util.List;
9import java.util.Set;
10
11import net.java.otr4j.OtrException;
12import net.java.otr4j.session.Session;
13import net.java.otr4j.session.SessionImpl;
14import net.java.otr4j.session.SessionStatus;
15
16import de.gultsch.chat.entities.Account;
17import de.gultsch.chat.entities.Contact;
18import de.gultsch.chat.entities.Conversation;
19import de.gultsch.chat.entities.Message;
20import de.gultsch.chat.entities.Presences;
21import de.gultsch.chat.persistance.DatabaseBackend;
22import de.gultsch.chat.ui.OnAccountListChangedListener;
23import de.gultsch.chat.ui.OnConversationListChangedListener;
24import de.gultsch.chat.ui.OnRosterFetchedListener;
25import de.gultsch.chat.utils.OnPhoneContactsLoadedListener;
26import de.gultsch.chat.utils.PhoneHelper;
27import de.gultsch.chat.utils.UIHelper;
28import de.gultsch.chat.xml.Element;
29import de.gultsch.chat.xmpp.IqPacket;
30import de.gultsch.chat.xmpp.MessagePacket;
31import de.gultsch.chat.xmpp.OnIqPacketReceived;
32import de.gultsch.chat.xmpp.OnMessagePacketReceived;
33import de.gultsch.chat.xmpp.OnPresencePacketReceived;
34import de.gultsch.chat.xmpp.OnStatusChanged;
35import de.gultsch.chat.xmpp.PresencePacket;
36import de.gultsch.chat.xmpp.XmppConnection;
37import android.app.NotificationManager;
38import android.app.Service;
39import android.content.Context;
40import android.content.Intent;
41import android.database.ContentObserver;
42import android.os.Binder;
43import android.os.Bundle;
44import android.os.IBinder;
45import android.os.PowerManager;
46import android.provider.ContactsContract;
47import android.util.Log;
48
49public class XmppConnectionService extends Service {
50
51 protected static final String LOGTAG = "xmppService";
52 protected DatabaseBackend databaseBackend;
53
54 public long startDate;
55
56 private List<Account> accounts;
57 private List<Conversation> conversations = null;
58
59 private OnConversationListChangedListener convChangedListener = null;
60 private OnAccountListChangedListener accountChangedListener = null;
61
62 private ContentObserver contactObserver = new ContentObserver(null) {
63 @Override
64 public void onChange(boolean selfChange) {
65 super.onChange(selfChange);
66 Log.d(LOGTAG, "contact list has changed");
67 mergePhoneContactsWithRoster();
68 }
69 };
70
71 private final IBinder mBinder = new XmppConnectionBinder();
72 private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
73
74 @Override
75 public void onMessagePacketReceived(Account account,
76 MessagePacket packet) {
77 if ((packet.getType() == MessagePacket.TYPE_CHAT)
78 || (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
79 boolean notify = true;
80 boolean runOtrCheck = false;
81 int status = Message.STATUS_RECIEVED;
82 int encryption = Message.ENCRYPTION_NONE;
83 String body;
84 String fullJid;
85 if (!packet.hasChild("body")) {
86 Element forwarded;
87 if (packet.hasChild("received")) {
88 forwarded = packet.findChild("received").findChild(
89 "forwarded");
90 } else if (packet.hasChild("sent")) {
91 forwarded = packet.findChild("sent").findChild(
92 "forwarded");
93 status = Message.STATUS_SEND;
94 notify = false;
95 } else {
96 return; // massage has no body and is not carbon. just
97 // skip
98 }
99 if (forwarded != null) {
100 Element message = forwarded.findChild("message");
101 if ((message == null) || (!message.hasChild("body")))
102 return; // either malformed or boring
103 if (status == Message.STATUS_RECIEVED) {
104 fullJid = message.getAttribute("from");
105 } else {
106 fullJid = message.getAttribute("to");
107 }
108 body = message.findChild("body").getContent();
109 } else {
110 return; // packet malformed. has no forwarded element
111 }
112 } else {
113 fullJid = packet.getFrom();
114 body = packet.getBody();
115 runOtrCheck = true;
116 }
117 Conversation conversation = null;
118 String[] fromParts = fullJid.split("/");
119 String jid = fromParts[0];
120 boolean muc = (packet.getType() == MessagePacket.TYPE_GROUPCHAT);
121 String counterPart = null;
122 conversation = findOrCreateConversation(account, jid, muc);
123 if (muc) {
124 if ((fromParts.length == 1) || (packet.hasChild("subject"))) {
125 return;
126 }
127 counterPart = fromParts[1];
128 if (counterPart.equals(account.getUsername())) {
129 status = Message.STATUS_SEND;
130 notify = false;
131 }
132 } else {
133 counterPart = fullJid;
134 if ((runOtrCheck) && body.startsWith("?OTR")) {
135 if (!conversation.hasValidOtrSession()) {
136 conversation.startOtrSession(
137 getApplicationContext(), fromParts[1]);
138 }
139 try {
140 Session otrSession = conversation.getOtrSession();
141 SessionStatus before = otrSession
142 .getSessionStatus();
143 body = otrSession.transformReceiving(body);
144 SessionStatus after = otrSession.getSessionStatus();
145 if ((before != after)
146 && (after == SessionStatus.ENCRYPTED)) {
147 Log.d(LOGTAG, "otr session etablished");
148 List<Message> messages = conversation
149 .getMessages();
150 for (int i = 0; i < messages.size(); ++i) {
151 Message msg = messages.get(i);
152 if ((msg.getStatus() == Message.STATUS_UNSEND)
153 && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
154 MessagePacket outPacket = prepareMessagePacket(
155 account, msg, otrSession);
156 msg.setStatus(Message.STATUS_SEND);
157 databaseBackend.updateMessage(msg);
158 account.getXmppConnection()
159 .sendMessagePacket(outPacket);
160 }
161 }
162 if (convChangedListener!=null) {
163 convChangedListener.onConversationListChanged();
164 }
165 } else if ((before != after) && (after == SessionStatus.FINISHED)) {
166 conversation.resetOtrSession();
167 Log.d(LOGTAG,"otr session stoped");
168 }
169 } catch (Exception e) {
170 Log.d(LOGTAG, "error receiving otr. resetting");
171 conversation.resetOtrSession();
172 return;
173 }
174 if (body == null) {
175 return;
176 }
177 encryption = Message.ENCRYPTION_OTR;
178 }
179 }
180 Message message = new Message(conversation, counterPart, body,
181 encryption, status);
182 if (packet.hasChild("delay")) {
183 try {
184 String stamp = packet.findChild("delay").getAttribute(
185 "stamp");
186 stamp = stamp.replace("Z", "+0000");
187 Date date = new SimpleDateFormat(
188 "yyyy-MM-dd'T'HH:mm:ssZ").parse(stamp);
189 message.setTime(date.getTime());
190 } catch (ParseException e) {
191 Log.d(LOGTAG,
192 "error trying to parse date" + e.getMessage());
193 }
194 }
195 if (notify) {
196 message.markUnread();
197 }
198 conversation.getMessages().add(message);
199 databaseBackend.createMessage(message);
200 if (convChangedListener != null) {
201 convChangedListener.onConversationListChanged();
202 } else {
203 if (notify) {
204 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
205 mNotificationManager.notify(2342, UIHelper
206 .getUnreadMessageNotification(
207 getApplicationContext(), conversation));
208 }
209 }
210 }
211 }
212 };
213 private OnStatusChanged statusListener = new OnStatusChanged() {
214
215 @Override
216 public void onStatusChanged(Account account) {
217 if (accountChangedListener != null) {
218 accountChangedListener.onAccountListChangedListener();
219 }
220 if (account.getStatus() == Account.STATUS_ONLINE) {
221 databaseBackend.clearPresences(account);
222 connectMultiModeConversations(account);
223 List<Conversation> conversations = getConversations();
224 for (int i = 0; i < conversations.size(); ++i) {
225 if (conversations.get(i).getAccount() == account) {
226 sendUnsendMessages(conversations.get(i));
227 }
228 }
229 if (convChangedListener != null) {
230 convChangedListener.onConversationListChanged();
231 }
232 }
233 }
234 };
235
236 private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() {
237
238 @Override
239 public void onPresencePacketReceived(Account account,
240 PresencePacket packet) {
241 String[] fromParts = packet.getAttribute("from").split("/");
242 Contact contact = findContact(account, fromParts[0]);
243 if (contact == null) {
244 // most likely muc, self or roster not synced
245 // Log.d(LOGTAG,"got presence for non contact "+packet.toString());
246 return;
247 }
248 String type = packet.getAttribute("type");
249 if (type == null) {
250 Element show = packet.findChild("show");
251 if (show == null) {
252 contact.updatePresence(fromParts[1], Presences.ONLINE);
253 } else if (show.getContent().equals("away")) {
254 contact.updatePresence(fromParts[1], Presences.AWAY);
255 } else if (show.getContent().equals("xa")) {
256 contact.updatePresence(fromParts[1], Presences.XA);
257 } else if (show.getContent().equals("chat")) {
258 contact.updatePresence(fromParts[1], Presences.CHAT);
259 } else if (show.getContent().equals("dnd")) {
260 contact.updatePresence(fromParts[1], Presences.DND);
261 }
262 databaseBackend.updateContact(contact);
263 } else if (type.equals("unavailable")) {
264 if (fromParts.length != 2) {
265 // Log.d(LOGTAG,"received presence with no resource "+packet.toString());
266 } else {
267 contact.removePresence(fromParts[1]);
268 databaseBackend.updateContact(contact);
269 }
270 }
271 replaceContactInConversation(contact);
272 }
273 };
274
275 private void replaceContactInConversation(Contact contact) {
276 List<Conversation> conversations = getConversations();
277 for(int i = 0; i < conversations.size(); ++i) {
278 if (conversations.get(i).getContact().equals(contact)) {
279 conversations.get(i).setContact(contact);
280 break;
281 }
282 }
283 }
284
285 public class XmppConnectionBinder extends Binder {
286 public XmppConnectionService getService() {
287 return XmppConnectionService.this;
288 }
289 }
290
291 @Override
292 public int onStartCommand(Intent intent, int flags, int startId) {
293 for (Account account : accounts) {
294 if (account.getXmppConnection() == null) {
295 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
296 account.setXmppConnection(this.createConnection(account));
297 }
298 }
299 }
300 return START_STICKY;
301 }
302
303 @Override
304 public void onCreate() {
305 databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
306 this.accounts = databaseBackend.getAccounts();
307
308 getContentResolver().registerContentObserver(
309 ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
310 }
311
312 @Override
313 public void onDestroy() {
314 super.onDestroy();
315 for (Account account : accounts) {
316 if (account.getXmppConnection() != null) {
317 disconnect(account);
318 }
319 }
320 }
321
322 public XmppConnection createConnection(Account account) {
323 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
324 XmppConnection connection = new XmppConnection(account, pm);
325 connection.setOnMessagePacketReceivedListener(this.messageListener);
326 connection.setOnStatusChangedListener(this.statusListener);
327 connection.setOnPresencePacketReceivedListener(this.presenceListener);
328 Thread thread = new Thread(connection);
329 thread.start();
330 return connection;
331 }
332
333 public void sendMessage(Account account, Message message, String presence) {
334 Conversation conv = message.getConversation();
335 boolean saveInDb = false;
336 boolean addToConversation = false;
337 if (account.getStatus() == Account.STATUS_ONLINE) {
338 MessagePacket packet;
339 if (message.getEncryption() == Message.ENCRYPTION_OTR) {
340 if (!conv.hasValidOtrSession()) {
341 //starting otr session. messages will be send later
342 conv.startOtrSession(getApplicationContext(), presence);
343 } else if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED){
344 //otr session aleary exists, creating message packet accordingly
345 packet = prepareMessagePacket(account, message,
346 conv.getOtrSession());
347 account.getXmppConnection().sendMessagePacket(packet);
348 message.setStatus(Message.STATUS_SEND);
349 }
350 saveInDb = true;
351 addToConversation = true;
352 } else {
353 // don't encrypt
354 if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
355 message.setStatus(Message.STATUS_SEND);
356 saveInDb = true;
357 addToConversation = true;
358 }
359
360 packet = prepareMessagePacket(account, message, null);
361 account.getXmppConnection().sendMessagePacket(packet);
362 }
363 } else {
364 // account is offline
365 saveInDb = true;
366 addToConversation = true;
367
368 }
369 if (saveInDb) {
370 databaseBackend.createMessage(message);
371 }
372 if (addToConversation) {
373 conv.getMessages().add(message);
374 if (convChangedListener != null) {
375 convChangedListener.onConversationListChanged();
376 }
377 }
378
379 }
380
381 private void sendUnsendMessages(Conversation conversation) {
382 for (int i = 0; i < conversation.getMessages().size(); ++i) {
383 if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
384 Message message = conversation.getMessages().get(i);
385 MessagePacket packet = prepareMessagePacket(
386 conversation.getAccount(), message, null);
387 conversation.getAccount().getXmppConnection()
388 .sendMessagePacket(packet);
389 message.setStatus(Message.STATUS_SEND);
390 if (conversation.getMode() == Conversation.MODE_SINGLE) {
391 databaseBackend.updateMessage(message);
392 } else {
393 databaseBackend.deleteMessage(message);
394 conversation.getMessages().remove(i);
395 i--;
396 }
397 }
398 }
399 }
400
401 private MessagePacket prepareMessagePacket(Account account,
402 Message message, Session otrSession) {
403 MessagePacket packet = new MessagePacket();
404 if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
405 packet.setType(MessagePacket.TYPE_CHAT);
406 if (otrSession != null) {
407 try {
408 packet.setBody(otrSession.transformSending(message
409 .getBody()));
410 } catch (OtrException e) {
411 Log.d(LOGTAG,
412 account.getJid()
413 + ": could not encrypt message to "
414 + message.getCounterpart());
415 }
416 Element privateMarker = new Element("private");
417 privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
418 packet.addChild(privateMarker);
419 packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID());
420 packet.setFrom(account.getFullJid());
421 } else {
422 packet.setBody(message.getBody());
423 packet.setTo(message.getCounterpart());
424 packet.setFrom(account.getJid());
425 }
426 } else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
427 packet.setType(MessagePacket.TYPE_GROUPCHAT);
428 packet.setBody(message.getBody());
429 packet.setTo(message.getCounterpart());
430 packet.setFrom(account.getJid());
431 }
432 return packet;
433 }
434
435 public void getRoster(Account account,
436 final OnRosterFetchedListener listener) {
437 List<Contact> contacts = databaseBackend.getContacts(account);
438 for (int i = 0; i < contacts.size(); ++i) {
439 contacts.get(i).setAccount(account);
440 }
441 if (listener != null) {
442 listener.onRosterFetched(contacts);
443 }
444 }
445
446 public void updateRoster(final Account account,
447 final OnRosterFetchedListener listener) {
448
449 PhoneHelper.loadPhoneContacts(this,
450 new OnPhoneContactsLoadedListener() {
451
452 @Override
453 public void onPhoneContactsLoaded(
454 final Hashtable<String, Bundle> phoneContacts) {
455 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
456 Element query = new Element("query");
457 query.setAttribute("xmlns", "jabber:iq:roster");
458 query.setAttribute("ver", "");
459 iqPacket.addChild(query);
460 account.getXmppConnection().sendIqPacket(iqPacket,
461 new OnIqPacketReceived() {
462
463 @Override
464 public void onIqPacketReceived(
465 Account account, IqPacket packet) {
466 List<Contact> contacts = new ArrayList<Contact>();
467 Element roster = packet
468 .findChild("query");
469 if (roster != null) {
470 for (Element item : roster
471 .getChildren()) {
472 Contact contact;
473 String name = item
474 .getAttribute("name");
475 String jid = item
476 .getAttribute("jid");
477 if (phoneContacts
478 .containsKey(jid)) {
479 Bundle phoneContact = phoneContacts
480 .get(jid);
481 String systemAccount = phoneContact
482 .getInt("phoneid")
483 + "#"
484 + phoneContact
485 .getString("lookup");
486 contact = new Contact(
487 account,
488 phoneContact
489 .getString("displayname"),
490 jid,
491 phoneContact
492 .getString("photouri"));
493 contact.setSystemAccount(systemAccount);
494 } else {
495 if (name == null) {
496 name = jid.split("@")[0];
497 }
498 contact = new Contact(
499 account, name, jid,
500 null);
501
502 }
503 contact.setAccount(account);
504 contact.setSubscription(item
505 .getAttribute("subscription"));
506 contacts.add(contact);
507 }
508 databaseBackend
509 .mergeContacts(contacts);
510 if (listener != null) {
511 listener.onRosterFetched(contacts);
512 }
513 }
514 }
515 });
516
517 }
518 });
519 }
520
521 public void mergePhoneContactsWithRoster() {
522 PhoneHelper.loadPhoneContacts(this,
523 new OnPhoneContactsLoadedListener() {
524 @Override
525 public void onPhoneContactsLoaded(
526 Hashtable<String, Bundle> phoneContacts) {
527 List<Contact> contacts = databaseBackend
528 .getContacts(null);
529 for (int i = 0; i < contacts.size(); ++i) {
530 Contact contact = contacts.get(i);
531 if (phoneContacts.containsKey(contact.getJid())) {
532 Bundle phoneContact = phoneContacts.get(contact
533 .getJid());
534 String systemAccount = phoneContact
535 .getInt("phoneid")
536 + "#"
537 + phoneContact.getString("lookup");
538 contact.setSystemAccount(systemAccount);
539 contact.setPhotoUri(phoneContact
540 .getString("photouri"));
541 contact.setDisplayName(phoneContact
542 .getString("displayname"));
543 databaseBackend.updateContact(contact);
544 } else {
545 if ((contact.getSystemAccount() != null)
546 || (contact.getProfilePhoto() != null)) {
547 contact.setSystemAccount(null);
548 contact.setPhotoUri(null);
549 databaseBackend.updateContact(contact);
550 }
551 }
552 }
553 }
554 });
555 }
556
557 public void addConversation(Conversation conversation) {
558 databaseBackend.createConversation(conversation);
559 }
560
561 public List<Conversation> getConversations() {
562 if (this.conversations == null) {
563 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
564 for (Account account : this.accounts) {
565 accountLookupTable.put(account.getUuid(), account);
566 }
567 this.conversations = databaseBackend
568 .getConversations(Conversation.STATUS_AVAILABLE);
569 for (Conversation conv : this.conversations) {
570 Account account = accountLookupTable.get(conv.getAccountUuid());
571 conv.setAccount(account);
572 conv.setContact(findContact(account, conv.getContactJid()));
573 conv.setMessages(databaseBackend.getMessages(conv, 50));
574 }
575 }
576 return this.conversations;
577 }
578
579 public List<Account> getAccounts() {
580 return this.accounts;
581 }
582
583 public Contact findContact(Account account, String jid) {
584 return databaseBackend.findContact(account, jid);
585 }
586
587 public Conversation findOrCreateConversation(Account account, String jid,
588 boolean muc) {
589 for (Conversation conv : this.getConversations()) {
590 if ((conv.getAccount().equals(account))
591 && (conv.getContactJid().equals(jid))) {
592 return conv;
593 }
594 }
595 Conversation conversation = databaseBackend.findConversation(account,
596 jid);
597 if (conversation != null) {
598 conversation.setStatus(Conversation.STATUS_AVAILABLE);
599 conversation.setAccount(account);
600 if (muc) {
601 conversation.setMode(Conversation.MODE_MULTI);
602 if (account.getStatus() == Account.STATUS_ONLINE) {
603 joinMuc(conversation);
604 }
605 } else {
606 conversation.setMode(Conversation.MODE_SINGLE);
607 }
608 this.databaseBackend.updateConversation(conversation);
609 conversation.setContact(findContact(account, conversation.getContactJid()));
610 } else {
611 String conversationName;
612 Contact contact = findContact(account, jid);
613 if (contact != null) {
614 conversationName = contact.getDisplayName();
615 } else {
616 conversationName = jid.split("@")[0];
617 }
618 if (muc) {
619 conversation = new Conversation(conversationName, account, jid,
620 Conversation.MODE_MULTI);
621 if (account.getStatus() == Account.STATUS_ONLINE) {
622 joinMuc(conversation);
623 }
624 } else {
625 conversation = new Conversation(conversationName, account, jid,
626 Conversation.MODE_SINGLE);
627 }
628 conversation.setContact(contact);
629 this.databaseBackend.createConversation(conversation);
630 }
631 this.conversations.add(conversation);
632 if (this.convChangedListener != null) {
633 this.convChangedListener.onConversationListChanged();
634 }
635 return conversation;
636 }
637
638 public void archiveConversation(Conversation conversation) {
639 if (conversation.getMode() == Conversation.MODE_MULTI) {
640 leaveMuc(conversation);
641 } else {
642 try {
643 conversation.endOtrIfNeeded();
644 } catch (OtrException e) {
645 Log.d(LOGTAG,
646 "error ending otr session for "
647 + conversation.getName());
648 }
649 }
650 this.databaseBackend.updateConversation(conversation);
651 this.conversations.remove(conversation);
652 if (this.convChangedListener != null) {
653 this.convChangedListener.onConversationListChanged();
654 }
655 }
656
657 public int getConversationCount() {
658 return this.databaseBackend.getConversationCount();
659 }
660
661 public void createAccount(Account account) {
662 databaseBackend.createAccount(account);
663 this.accounts.add(account);
664 account.setXmppConnection(this.createConnection(account));
665 if (accountChangedListener != null)
666 accountChangedListener.onAccountListChangedListener();
667 }
668
669 public void updateAccount(Account account) {
670 databaseBackend.updateAccount(account);
671 if (account.getXmppConnection() != null) {
672 disconnect(account);
673 }
674 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
675 account.setXmppConnection(this.createConnection(account));
676 }
677 if (accountChangedListener != null)
678 accountChangedListener.onAccountListChangedListener();
679 }
680
681 public void deleteAccount(Account account) {
682 Log.d(LOGTAG, "called delete account");
683 if (account.getXmppConnection() != null) {
684 this.disconnect(account);
685 }
686 databaseBackend.deleteAccount(account);
687 this.accounts.remove(account);
688 if (accountChangedListener != null)
689 accountChangedListener.onAccountListChangedListener();
690 }
691
692 public void setOnConversationListChangedListener(
693 OnConversationListChangedListener listener) {
694 this.convChangedListener = listener;
695 }
696
697 public void removeOnConversationListChangedListener() {
698 this.convChangedListener = null;
699 }
700
701 public void setOnAccountListChangedListener(
702 OnAccountListChangedListener listener) {
703 this.accountChangedListener = listener;
704 }
705
706 public void removeOnAccountListChangedListener() {
707 this.accountChangedListener = null;
708 }
709
710 public void connectMultiModeConversations(Account account) {
711 List<Conversation> conversations = getConversations();
712 for (int i = 0; i < conversations.size(); i++) {
713 Conversation conversation = conversations.get(i);
714 if ((conversation.getMode() == Conversation.MODE_MULTI)
715 && (conversation.getAccount() == account)) {
716 joinMuc(conversation);
717 }
718 }
719 }
720
721 public void joinMuc(Conversation conversation) {
722 String muc = conversation.getContactJid();
723 PresencePacket packet = new PresencePacket();
724 packet.setAttribute("to", muc + "/"
725 + conversation.getAccount().getUsername());
726 Element x = new Element("x");
727 x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
728 if (conversation.getMessages().size() != 0) {
729 Element history = new Element("history");
730 long lastMsgTime = conversation.getLatestMessage().getTimeSent();
731 long diff = (System.currentTimeMillis() - lastMsgTime) / 1000;
732 history.setAttribute("seconds",diff+"");
733 x.addChild(history);
734 }
735 packet.addChild(x);
736 conversation.getAccount().getXmppConnection()
737 .sendPresencePacket(packet);
738 }
739
740 public void leaveMuc(Conversation conversation) {
741
742 }
743
744 public void disconnect(Account account) {
745 List<Conversation> conversations = getConversations();
746 for (int i = 0; i < conversations.size(); i++) {
747 Conversation conversation = conversations.get(i);
748 if (conversation.getAccount() == account) {
749 if (conversation.getMode() == Conversation.MODE_MULTI) {
750 leaveMuc(conversation);
751 } else {
752 try {
753 conversation.endOtrIfNeeded();
754 } catch (OtrException e) {
755 Log.d(LOGTAG, "error ending otr session for "
756 + conversation.getName());
757 }
758 }
759 }
760 }
761 account.getXmppConnection().disconnect();
762 Log.d(LOGTAG, "disconnected account: " + account.getJid());
763 account.setXmppConnection(null);
764 }
765
766 @Override
767 public IBinder onBind(Intent intent) {
768 return mBinder;
769 }
770
771 public void updateContact(Contact contact) {
772 databaseBackend.updateContact(contact);
773 }
774}