1package de.gultsch.chat.services;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.Hashtable;
6import java.util.List;
7
8
9import de.gultsch.chat.entities.Account;
10import de.gultsch.chat.entities.Contact;
11import de.gultsch.chat.entities.Conversation;
12import de.gultsch.chat.entities.Message;
13import de.gultsch.chat.persistance.DatabaseBackend;
14import de.gultsch.chat.ui.OnConversationListChangedListener;
15import de.gultsch.chat.ui.OnRosterFetchedListener;
16import de.gultsch.chat.xml.Element;
17import de.gultsch.chat.xmpp.IqPacket;
18import de.gultsch.chat.xmpp.MessagePacket;
19import de.gultsch.chat.xmpp.OnIqPacketReceived;
20import de.gultsch.chat.xmpp.OnMessagePacketReceived;
21import de.gultsch.chat.xmpp.XmppConnection;
22import android.app.Service;
23import android.content.Context;
24import android.content.Intent;
25import android.os.Binder;
26import android.os.IBinder;
27import android.os.PowerManager;
28import android.util.Log;
29
30public class XmppConnectionService extends Service {
31
32 protected static final String LOGTAG = "xmppService";
33 protected DatabaseBackend databaseBackend;
34
35 public long startDate;
36
37 private List<Account> accounts;
38 private List<Conversation> conversations = null;
39
40 private Hashtable<Account,XmppConnection> connections = new Hashtable<Account, XmppConnection>();
41
42 private OnConversationListChangedListener convChangedListener = null;
43
44 private final IBinder mBinder = new XmppConnectionBinder();
45 private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
46
47 @Override
48 public void onMessagePacketReceived(Account account, MessagePacket packet) {
49 String fullJid = packet.getFrom();
50 String jid = fullJid.split("/")[0];
51 String name = jid.split("@")[0];
52 Log.d(LOGTAG,"message received for "+account.getJid()+" from "+jid);
53 Log.d(LOGTAG,packet.toString());
54 Contact contact = new Contact(account,name,jid,null); //dummy contact
55 Conversation conversation = findOrCreateConversation(account, contact);
56 Message message = new Message(conversation, fullJid, packet.getBody(), Message.ENCRYPTION_NONE, Message.STATUS_RECIEVED);
57 conversation.getMessages().add(message);
58 databaseBackend.createMessage(message);
59 if (convChangedListener != null) {
60 convChangedListener.onConversationListChanged();
61 }
62 }
63 };
64
65 public class XmppConnectionBinder extends Binder {
66 public XmppConnectionService getService() {
67 return XmppConnectionService.this;
68 }
69 }
70
71 @Override
72 public int onStartCommand(Intent intent, int flags, int startId) {
73 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
74 for(Account account : accounts) {
75 if (!connections.containsKey(account)) {
76 XmppConnection connection = new XmppConnection(account, pm);
77 connection.setOnMessagePacketReceivedListener(this.messageListener );
78 Thread thread = new Thread(connection);
79 thread.start();
80 this.connections.put(account, connection);
81 }
82 }
83 return START_STICKY;
84 }
85
86 @Override
87 public void onCreate() {
88 databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
89 this.accounts = databaseBackend.getAccounts();
90 }
91
92 @Override
93 public IBinder onBind(Intent intent) {
94 return mBinder;
95 }
96
97 public void sendMessage(final Account account, final Message message) {
98 new Thread() {
99 @Override
100 public void run() {
101 Log.d(LOGTAG,"sending message for "+account.getJid()+" to: "+message.getCounterpart());
102 databaseBackend.createMessage(message);
103 MessagePacket packet = new MessagePacket();
104 packet.setType(MessagePacket.TYPE_CHAT);
105 packet.setTo(message.getCounterpart());
106 packet.setFrom(account.getJid());
107 packet.setBody(message.getBody());
108 try {
109 connections.get(account).sendMessagePacket(packet);
110 message.setStatus(Message.STATUS_SEND);
111 databaseBackend.updateMessage(message);
112 } catch (IOException e) {
113 Log.d(LOGTAG,"io exception during send. message is in database. will try again later");
114 }
115 }
116 }.start();
117 }
118
119 public void getRoster(final Account account, final OnRosterFetchedListener listener) {
120 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
121 Element query = new Element("query");
122 query.setAttribute("xmlns", "jabber:iq:roster");
123 query.setAttribute("ver", "");
124 iqPacket.addChild(query);
125 try {
126 connections.get(account).sendIqPacket(iqPacket, new OnIqPacketReceived() {
127
128 @Override
129 public void onIqPacketReceived(Account account, IqPacket packet) {
130 Element roster = packet.findChild("query");
131 Log.d(LOGTAG,roster.toString());
132 List<Contact> contacts = new ArrayList<Contact>();
133 for(Element item : roster.getChildren()) {
134 String name = item.getAttribute("name");
135 String jid = item.getAttribute("jid");
136 if (name==null) {
137 name = jid.split("@")[0];
138 }
139 Contact contact = new Contact(account, name, jid, null);
140 contacts.add(contact);
141 }
142 if (listener != null) {
143 listener.onRosterFetched(contacts);
144 }
145 }
146 });
147 } catch (IOException e) {
148 Log.d(LOGTAG,"io error during roster fetch");
149 }
150 }
151
152 public void addConversation(Conversation conversation) {
153 databaseBackend.createConversation(conversation);
154 }
155
156 public List<Conversation> getConversations() {
157 if (this.conversations == null) {
158 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
159 for(Account account : this.accounts) {
160 accountLookupTable.put(account.getUuid(), account);
161 }
162 this.conversations = databaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
163 for(Conversation conv : this.conversations) {
164 conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
165 }
166 }
167 return this.conversations;
168 }
169
170 public List<Account> getAccounts() {
171 return this.accounts;
172 }
173
174 public List<Message> getMessages(Conversation conversation) {
175 return databaseBackend.getMessages(conversation, 100);
176 }
177
178 public Conversation findOrCreateConversation(Account account, Contact contact) {
179 Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
180 for(Conversation conv : this.getConversations()) {
181 if ((conv.getAccount().equals(account))&&(conv.getContactJid().equals(contact.getJid()))) {
182 Log.d(LOGTAG,"found one in memory");
183 return conv;
184 }
185 }
186 Conversation conversation = databaseBackend.findConversation(account, contact.getJid());
187 if (conversation!=null) {
188 Log.d("gultsch","found one. unarchive it");
189 conversation.setStatus(Conversation.STATUS_AVAILABLE);
190 conversation.setAccount(account);
191 this.databaseBackend.updateConversation(conversation);
192 } else {
193 Log.d(LOGTAG,"didnt find one in archive. create new one");
194 conversation = new Conversation(contact.getDisplayName(), contact.getProfilePhoto(), account, contact.getJid());
195 this.databaseBackend.createConversation(conversation);
196 }
197 this.conversations.add(conversation);
198 if (this.convChangedListener != null) {
199 this.convChangedListener.onConversationListChanged();
200 }
201 return conversation;
202 }
203
204 public void archiveConversation(Conversation conversation) {
205 this.databaseBackend.updateConversation(conversation);
206 this.conversations.remove(conversation);
207 if (this.convChangedListener != null) {
208 this.convChangedListener.onConversationListChanged();
209 }
210 }
211
212 public int getConversationCount() {
213 return this.databaseBackend.getConversationCount();
214 }
215
216 public void createAccount(Account account) {
217 databaseBackend.createAccount(account);
218 }
219
220 public void updateAccount(Account account) {
221 databaseBackend.updateAccount(account);
222 }
223
224 public void deleteAccount(Account account) {
225 databaseBackend.deleteAccount(account);
226 }
227
228 public void setOnConversationListChangedListener(OnConversationListChangedListener listener) {
229 this.convChangedListener = listener;
230 }
231
232 public void removeOnConversationListChangedListener() {
233 this.convChangedListener = null;
234 }
235}