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