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.hasOtrSession()) {
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 }
166 } catch (Exception e) {
167 Log.d(LOGTAG, "error receiving otr. resetting");
168 conversation.resetOtrSession();
169 return;
170 }
171 if (body == null) {
172 return;
173 }
174 encryption = Message.ENCRYPTION_OTR;
175 }
176 }
177 Message message = new Message(conversation, counterPart, body,
178 encryption, status);
179 if (packet.hasChild("delay")) {
180 try {
181 String stamp = packet.findChild("delay").getAttribute(
182 "stamp");
183 stamp = stamp.replace("Z", "+0000");
184 Date date = new SimpleDateFormat(
185 "yyyy-MM-dd'T'HH:mm:ssZ").parse(stamp);
186 message.setTime(date.getTime());
187 } catch (ParseException e) {
188 Log.d(LOGTAG,
189 "error trying to parse date" + e.getMessage());
190 }
191 }
192 if (notify) {
193 message.markUnread();
194 }
195 conversation.getMessages().add(message);
196 databaseBackend.createMessage(message);
197 if (convChangedListener != null) {
198 convChangedListener.onConversationListChanged();
199 } else {
200 if (notify) {
201 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
202 mNotificationManager.notify(2342, UIHelper
203 .getUnreadMessageNotification(
204 getApplicationContext(), conversation));
205 }
206 }
207 }
208 }
209 };
210 private OnStatusChanged statusListener = new OnStatusChanged() {
211
212 @Override
213 public void onStatusChanged(Account account) {
214 if (accountChangedListener != null) {
215 accountChangedListener.onAccountListChangedListener();
216 }
217 if (account.getStatus() == Account.STATUS_ONLINE) {
218 databaseBackend.clearPresences(account);
219 connectMultiModeConversations(account);
220 List<Conversation> conversations = getConversations();
221 for (int i = 0; i < conversations.size(); ++i) {
222 if (conversations.get(i).getAccount() == account) {
223 sendUnsendMessages(conversations.get(i));
224 }
225 }
226 if (convChangedListener != null) {
227 convChangedListener.onConversationListChanged();
228 }
229 }
230 }
231 };
232
233 private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() {
234
235 @Override
236 public void onPresencePacketReceived(Account account,
237 PresencePacket packet) {
238 String[] fromParts = packet.getAttribute("from").split("/");
239 Contact contact = findContact(account, fromParts[0]);
240 if (contact == null) {
241 // most likely muc, self or roster not synced
242 // Log.d(LOGTAG,"got presence for non contact "+packet.toString());
243 return;
244 }
245 String type = packet.getAttribute("type");
246 if (type == null) {
247 Element show = packet.findChild("show");
248 if (show == null) {
249 contact.updatePresence(fromParts[1], Presences.ONLINE);
250 } else if (show.getContent().equals("away")) {
251 contact.updatePresence(fromParts[1], Presences.AWAY);
252 } else if (show.getContent().equals("xa")) {
253 contact.updatePresence(fromParts[1], Presences.XA);
254 } else if (show.getContent().equals("chat")) {
255 contact.updatePresence(fromParts[1], Presences.CHAT);
256 } else if (show.getContent().equals("dnd")) {
257 contact.updatePresence(fromParts[1], Presences.DND);
258 }
259 databaseBackend.updateContact(contact);
260 } else if (type.equals("unavailable")) {
261 if (fromParts.length != 2) {
262 // Log.d(LOGTAG,"received presence with no resource "+packet.toString());
263 } else {
264 contact.removePresence(fromParts[1]);
265 databaseBackend.updateContact(contact);
266 }
267 }
268 replaceContactInConversation(contact);
269 }
270 };
271
272 private void replaceContactInConversation(Contact contact) {
273 List<Conversation> conversations = getConversations();
274 for(int i = 0; i < conversations.size(); ++i) {
275 if (conversations.get(i).getContact().equals(contact)) {
276 conversations.get(i).setContact(contact);
277 break;
278 }
279 }
280 }
281
282 public class XmppConnectionBinder extends Binder {
283 public XmppConnectionService getService() {
284 return XmppConnectionService.this;
285 }
286 }
287
288 @Override
289 public int onStartCommand(Intent intent, int flags, int startId) {
290 for (Account account : accounts) {
291 if (account.getXmppConnection() == null) {
292 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
293 account.setXmppConnection(this.createConnection(account));
294 }
295 }
296 }
297 return START_STICKY;
298 }
299
300 @Override
301 public void onCreate() {
302 databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
303 this.accounts = databaseBackend.getAccounts();
304
305 getContentResolver().registerContentObserver(
306 ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
307 }
308
309 @Override
310 public void onDestroy() {
311 super.onDestroy();
312 for (Account account : accounts) {
313 if (account.getXmppConnection() != null) {
314 disconnect(account);
315 }
316 }
317 }
318
319 public XmppConnection createConnection(Account account) {
320 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
321 XmppConnection connection = new XmppConnection(account, pm);
322 connection.setOnMessagePacketReceivedListener(this.messageListener);
323 connection.setOnStatusChangedListener(this.statusListener);
324 connection.setOnPresencePacketReceivedListener(this.presenceListener);
325 Thread thread = new Thread(connection);
326 thread.start();
327 return connection;
328 }
329
330 private void startOtrSession(Conversation conv) {
331 Set<String> presences = conv.getContact().getPresences()
332 .keySet();
333 if (presences.size() == 0) {
334 Log.d(LOGTAG, "counter part isnt online. cant use otr");
335 return;
336 } else if (presences.size() == 1) {
337 conv.startOtrSession(getApplicationContext(),
338 (String) presences.toArray()[0]);
339 try {
340 conv.getOtrSession().startSession();
341 } catch (OtrException e) {
342 Log.d(LOGTAG, "couldnt actually start");
343 }
344 } else {
345 String latestCounterpartPresence = null;
346 List<Message> messages = conv.getMessages();
347 for (int i = messages.size() - 1; i >= 0; --i) {
348 if (messages.get(i).getStatus() == Message.STATUS_RECIEVED) {
349 String[] parts = messages.get(i).getCounterpart()
350 .split("/");
351 if (parts.length == 2) {
352 latestCounterpartPresence = parts[1];
353 break;
354 }
355 }
356 }
357 if (presences.contains(latestCounterpartPresence)) {
358 conv.startOtrSession(getApplicationContext(),
359 latestCounterpartPresence);
360 try {
361 conv.getOtrSession().startSession();
362 } catch (OtrException e) {
363 // TODO Auto-generated catch block
364 Log.d(LOGTAG, "couldnt actually start");
365 }
366 } else {
367 Log.d(LOGTAG,
368 "could not decide where to send otr connection to");
369 }
370 }
371 }
372
373 public void sendMessage(Account account, Message message) {
374 Conversation conv = message.getConversation();
375 boolean saveInDb = false;
376 boolean addToConversation = false;
377 if (account.getStatus() == Account.STATUS_ONLINE) {
378 MessagePacket packet;
379 if (message.getEncryption() == Message.ENCRYPTION_OTR) {
380 if (!conv.hasOtrSession()) {
381 //starting otr session. messages will be send later
382 startOtrSession(conv);
383 } else {
384 //otr session aleary exists, creating message packet accordingly
385 packet = prepareMessagePacket(account, message,
386 conv.getOtrSession());
387 account.getXmppConnection().sendMessagePacket(packet);
388 message.setStatus(Message.STATUS_SEND);
389 }
390 saveInDb = true;
391 addToConversation = true;
392 } else {
393 // don't encrypt
394 if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
395 message.setStatus(Message.STATUS_SEND);
396 saveInDb = true;
397 addToConversation = true;
398 }
399
400 packet = prepareMessagePacket(account, message, null);
401 account.getXmppConnection().sendMessagePacket(packet);
402 }
403 } else {
404 // account is offline
405 saveInDb = true;
406 addToConversation = true;
407
408 }
409 if (saveInDb) {
410 databaseBackend.createMessage(message);
411 }
412 if (addToConversation) {
413 conv.getMessages().add(message);
414 if (convChangedListener != null) {
415 convChangedListener.onConversationListChanged();
416 }
417 }
418
419 }
420
421 private void sendUnsendMessages(Conversation conversation) {
422 for (int i = 0; i < conversation.getMessages().size(); ++i) {
423 if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
424 Message message = conversation.getMessages().get(i);
425 MessagePacket packet = prepareMessagePacket(
426 conversation.getAccount(), message, null);
427 conversation.getAccount().getXmppConnection()
428 .sendMessagePacket(packet);
429 message.setStatus(Message.STATUS_SEND);
430 if (conversation.getMode() == Conversation.MODE_SINGLE) {
431 databaseBackend.updateMessage(message);
432 } else {
433 databaseBackend.deleteMessage(message);
434 conversation.getMessages().remove(i);
435 i--;
436 }
437 }
438 }
439 }
440
441 private MessagePacket prepareMessagePacket(Account account,
442 Message message, Session otrSession) {
443 MessagePacket packet = new MessagePacket();
444 if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
445 packet.setType(MessagePacket.TYPE_CHAT);
446 if (otrSession != null) {
447 try {
448 packet.setBody(otrSession.transformSending(message
449 .getBody()));
450 } catch (OtrException e) {
451 Log.d(LOGTAG,
452 account.getJid()
453 + ": could not encrypt message to "
454 + message.getCounterpart());
455 }
456 Element privateMarker = new Element("private");
457 privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
458 packet.addChild(privateMarker);
459 packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID());
460 packet.setFrom(account.getFullJid());
461 } else {
462 packet.setBody(message.getBody());
463 packet.setTo(message.getCounterpart());
464 packet.setFrom(account.getJid());
465 }
466 } else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
467 packet.setType(MessagePacket.TYPE_GROUPCHAT);
468 packet.setBody(message.getBody());
469 packet.setTo(message.getCounterpart());
470 packet.setFrom(account.getJid());
471 }
472 return packet;
473 }
474
475 public void getRoster(Account account,
476 final OnRosterFetchedListener listener) {
477 List<Contact> contacts = databaseBackend.getContacts(account);
478 for (int i = 0; i < contacts.size(); ++i) {
479 contacts.get(i).setAccount(account);
480 }
481 if (listener != null) {
482 listener.onRosterFetched(contacts);
483 }
484 }
485
486 public void updateRoster(final Account account,
487 final OnRosterFetchedListener listener) {
488
489 PhoneHelper.loadPhoneContacts(this,
490 new OnPhoneContactsLoadedListener() {
491
492 @Override
493 public void onPhoneContactsLoaded(
494 final Hashtable<String, Bundle> phoneContacts) {
495 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
496 Element query = new Element("query");
497 query.setAttribute("xmlns", "jabber:iq:roster");
498 query.setAttribute("ver", "");
499 iqPacket.addChild(query);
500 account.getXmppConnection().sendIqPacket(iqPacket,
501 new OnIqPacketReceived() {
502
503 @Override
504 public void onIqPacketReceived(
505 Account account, IqPacket packet) {
506 List<Contact> contacts = new ArrayList<Contact>();
507 Element roster = packet
508 .findChild("query");
509 if (roster != null) {
510 for (Element item : roster
511 .getChildren()) {
512 Contact contact;
513 String name = item
514 .getAttribute("name");
515 String jid = item
516 .getAttribute("jid");
517 if (phoneContacts
518 .containsKey(jid)) {
519 Bundle phoneContact = phoneContacts
520 .get(jid);
521 String systemAccount = phoneContact
522 .getInt("phoneid")
523 + "#"
524 + phoneContact
525 .getString("lookup");
526 contact = new Contact(
527 account,
528 phoneContact
529 .getString("displayname"),
530 jid,
531 phoneContact
532 .getString("photouri"));
533 contact.setSystemAccount(systemAccount);
534 } else {
535 if (name == null) {
536 name = jid.split("@")[0];
537 }
538 contact = new Contact(
539 account, name, jid,
540 null);
541
542 }
543 contact.setAccount(account);
544 contact.setSubscription(item
545 .getAttribute("subscription"));
546 contacts.add(contact);
547 }
548 databaseBackend
549 .mergeContacts(contacts);
550 if (listener != null) {
551 listener.onRosterFetched(contacts);
552 }
553 }
554 }
555 });
556
557 }
558 });
559 }
560
561 public void mergePhoneContactsWithRoster() {
562 PhoneHelper.loadPhoneContacts(this,
563 new OnPhoneContactsLoadedListener() {
564 @Override
565 public void onPhoneContactsLoaded(
566 Hashtable<String, Bundle> phoneContacts) {
567 List<Contact> contacts = databaseBackend
568 .getContacts(null);
569 for (int i = 0; i < contacts.size(); ++i) {
570 Contact contact = contacts.get(i);
571 if (phoneContacts.containsKey(contact.getJid())) {
572 Bundle phoneContact = phoneContacts.get(contact
573 .getJid());
574 String systemAccount = phoneContact
575 .getInt("phoneid")
576 + "#"
577 + phoneContact.getString("lookup");
578 contact.setSystemAccount(systemAccount);
579 contact.setPhotoUri(phoneContact
580 .getString("photouri"));
581 contact.setDisplayName(phoneContact
582 .getString("displayname"));
583 databaseBackend.updateContact(contact);
584 } else {
585 if ((contact.getSystemAccount() != null)
586 || (contact.getProfilePhoto() != null)) {
587 contact.setSystemAccount(null);
588 contact.setPhotoUri(null);
589 databaseBackend.updateContact(contact);
590 }
591 }
592 }
593 }
594 });
595 }
596
597 public void addConversation(Conversation conversation) {
598 databaseBackend.createConversation(conversation);
599 }
600
601 public List<Conversation> getConversations() {
602 if (this.conversations == null) {
603 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
604 for (Account account : this.accounts) {
605 accountLookupTable.put(account.getUuid(), account);
606 }
607 this.conversations = databaseBackend
608 .getConversations(Conversation.STATUS_AVAILABLE);
609 for (Conversation conv : this.conversations) {
610 Account account = accountLookupTable.get(conv.getAccountUuid());
611 conv.setAccount(account);
612 conv.setContact(findContact(account, conv.getContactJid()));
613 conv.setMessages(databaseBackend.getMessages(conv, 50));
614 }
615 }
616 return this.conversations;
617 }
618
619 public List<Account> getAccounts() {
620 return this.accounts;
621 }
622
623 public Contact findContact(Account account, String jid) {
624 return databaseBackend.findContact(account, jid);
625 }
626
627 public Conversation findOrCreateConversation(Account account, String jid,
628 boolean muc) {
629 for (Conversation conv : this.getConversations()) {
630 if ((conv.getAccount().equals(account))
631 && (conv.getContactJid().equals(jid))) {
632 return conv;
633 }
634 }
635 Conversation conversation = databaseBackend.findConversation(account,
636 jid);
637 if (conversation != null) {
638 conversation.setStatus(Conversation.STATUS_AVAILABLE);
639 conversation.setAccount(account);
640 if (muc) {
641 conversation.setMode(Conversation.MODE_MULTI);
642 if (account.getStatus() == Account.STATUS_ONLINE) {
643 joinMuc(conversation);
644 }
645 } else {
646 conversation.setMode(Conversation.MODE_SINGLE);
647 }
648 this.databaseBackend.updateConversation(conversation);
649 } else {
650 String conversationName;
651 Contact contact = findContact(account, jid);
652 if (contact != null) {
653 conversationName = contact.getDisplayName();
654 } else {
655 conversationName = jid.split("@")[0];
656 }
657 if (muc) {
658 conversation = new Conversation(conversationName, account, jid,
659 Conversation.MODE_MULTI);
660 if (account.getStatus() == Account.STATUS_ONLINE) {
661 joinMuc(conversation);
662 }
663 } else {
664 conversation = new Conversation(conversationName, account, jid,
665 Conversation.MODE_SINGLE);
666 }
667 conversation.setContact(contact);
668 this.databaseBackend.createConversation(conversation);
669 }
670 this.conversations.add(conversation);
671 if (this.convChangedListener != null) {
672 this.convChangedListener.onConversationListChanged();
673 }
674 return conversation;
675 }
676
677 public void archiveConversation(Conversation conversation) {
678 if (conversation.getMode() == Conversation.MODE_MULTI) {
679 leaveMuc(conversation);
680 } else {
681 try {
682 conversation.endOtrIfNeeded();
683 } catch (OtrException e) {
684 Log.d(LOGTAG,
685 "error ending otr session for "
686 + conversation.getName());
687 }
688 }
689 this.databaseBackend.updateConversation(conversation);
690 this.conversations.remove(conversation);
691 if (this.convChangedListener != null) {
692 this.convChangedListener.onConversationListChanged();
693 }
694 }
695
696 public int getConversationCount() {
697 return this.databaseBackend.getConversationCount();
698 }
699
700 public void createAccount(Account account) {
701 databaseBackend.createAccount(account);
702 this.accounts.add(account);
703 account.setXmppConnection(this.createConnection(account));
704 if (accountChangedListener != null)
705 accountChangedListener.onAccountListChangedListener();
706 }
707
708 public void updateAccount(Account account) {
709 databaseBackend.updateAccount(account);
710 if (account.getXmppConnection() != null) {
711 disconnect(account);
712 }
713 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
714 account.setXmppConnection(this.createConnection(account));
715 }
716 if (accountChangedListener != null)
717 accountChangedListener.onAccountListChangedListener();
718 }
719
720 public void deleteAccount(Account account) {
721 Log.d(LOGTAG, "called delete account");
722 if (account.getXmppConnection() != null) {
723 this.disconnect(account);
724 }
725 databaseBackend.deleteAccount(account);
726 this.accounts.remove(account);
727 if (accountChangedListener != null)
728 accountChangedListener.onAccountListChangedListener();
729 }
730
731 public void setOnConversationListChangedListener(
732 OnConversationListChangedListener listener) {
733 this.convChangedListener = listener;
734 }
735
736 public void removeOnConversationListChangedListener() {
737 this.convChangedListener = null;
738 }
739
740 public void setOnAccountListChangedListener(
741 OnAccountListChangedListener listener) {
742 this.accountChangedListener = listener;
743 }
744
745 public void removeOnAccountListChangedListener() {
746 this.accountChangedListener = null;
747 }
748
749 public void connectMultiModeConversations(Account account) {
750 List<Conversation> conversations = getConversations();
751 for (int i = 0; i < conversations.size(); i++) {
752 Conversation conversation = conversations.get(i);
753 if ((conversation.getMode() == Conversation.MODE_MULTI)
754 && (conversation.getAccount() == account)) {
755 joinMuc(conversation);
756 }
757 }
758 }
759
760 public void joinMuc(Conversation conversation) {
761 String muc = conversation.getContactJid();
762 PresencePacket packet = new PresencePacket();
763 packet.setAttribute("to", muc + "/"
764 + conversation.getAccount().getUsername());
765 Element x = new Element("x");
766 x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
767 if (conversation.getMessages().size() != 0) {
768 Element history = new Element("history");
769 history.setAttribute("seconds",
770 (System.currentTimeMillis() - conversation
771 .getLatestMessage().getTimeSent() / 1000) + "");
772 x.addChild(history);
773 }
774 packet.addChild(x);
775 conversation.getAccount().getXmppConnection()
776 .sendPresencePacket(packet);
777 }
778
779 public void leaveMuc(Conversation conversation) {
780
781 }
782
783 public void disconnect(Account account) {
784 List<Conversation> conversations = getConversations();
785 for (int i = 0; i < conversations.size(); i++) {
786 Conversation conversation = conversations.get(i);
787 if (conversation.getAccount() == account) {
788 if (conversation.getMode() == Conversation.MODE_MULTI) {
789 leaveMuc(conversation);
790 } else {
791 try {
792 conversation.endOtrIfNeeded();
793 } catch (OtrException e) {
794 Log.d(LOGTAG, "error ending otr session for "
795 + conversation.getName());
796 }
797 }
798 }
799 }
800 account.getXmppConnection().disconnect();
801 Log.d(LOGTAG, "disconnected account: " + account.getJid());
802 account.setXmppConnection(null);
803 }
804
805 @Override
806 public IBinder onBind(Intent intent) {
807 return mBinder;
808 }
809}