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