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