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