1package de.gultsch.chat.services;
2
3import java.util.ArrayList;
4import java.util.Hashtable;
5import java.util.List;
6
7import de.gultsch.chat.entities.Account;
8import de.gultsch.chat.entities.Contact;
9import de.gultsch.chat.entities.Conversation;
10import de.gultsch.chat.entities.Message;
11import de.gultsch.chat.persistance.DatabaseBackend;
12import de.gultsch.chat.ui.OnAccountListChangedListener;
13import de.gultsch.chat.ui.OnConversationListChangedListener;
14import de.gultsch.chat.ui.OnRosterFetchedListener;
15import de.gultsch.chat.utils.UIHelper;
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.OnStatusChanged;
22import de.gultsch.chat.xmpp.XmppConnection;
23import android.app.NotificationManager;
24import android.app.Service;
25import android.content.Context;
26import android.content.Intent;
27import android.os.Binder;
28import android.os.IBinder;
29import android.os.PowerManager;
30import android.util.Log;
31
32public class XmppConnectionService extends Service {
33
34 protected static final String LOGTAG = "xmppService";
35 protected DatabaseBackend databaseBackend;
36
37 public long startDate;
38
39 private List<Account> accounts;
40 private List<Conversation> conversations = null;
41
42 private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
43
44 private OnConversationListChangedListener convChangedListener = null;
45 private OnAccountListChangedListener accountChangedListener = null;
46
47 private final IBinder mBinder = new XmppConnectionBinder();
48 private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
49
50 @Override
51 public void onMessagePacketReceived(Account account,
52 MessagePacket packet) {
53 if (packet.getType() == MessagePacket.TYPE_CHAT) {
54 String fullJid = packet.getFrom();
55 String jid = fullJid.split("/")[0];
56 String name = jid.split("@")[0];
57 Contact contact = new Contact(account, name, jid, null); // dummy
58 // contact
59 Conversation conversation = findOrCreateConversation(account,
60 contact);
61 Message message = new Message(conversation, fullJid,
62 packet.getBody(), Message.ENCRYPTION_NONE,
63 Message.STATUS_RECIEVED);
64 conversation.getMessages().add(message);
65 databaseBackend.createMessage(message);
66 if (convChangedListener != null) {
67 convChangedListener.onConversationListChanged();
68 } else {
69 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
70 mNotificationManager.notify(2342, UIHelper
71 .getUnreadMessageNotification(
72 getApplicationContext(), conversation));
73 }
74 }
75 }
76 };
77 private OnStatusChanged statusListener = new OnStatusChanged() {
78
79 @Override
80 public void onStatusChanged(Account account) {
81 Log.d(LOGTAG,account.getJid()+" changed status to "+account.getStatus());
82 if (accountChangedListener != null) {
83 accountChangedListener.onAccountListChangedListener();
84 }
85 }
86 };
87
88 public class XmppConnectionBinder extends Binder {
89 public XmppConnectionService getService() {
90 return XmppConnectionService.this;
91 }
92 }
93
94 @Override
95 public int onStartCommand(Intent intent, int flags, int startId) {
96 for (Account account : accounts) {
97 if (!connections.containsKey(account)) {
98 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
99 this.connections.put(account, this.createConnection(account));
100 } else {
101 Log.d(LOGTAG,account.getJid()+": not starting because it's disabled");
102 }
103 }
104 }
105 return START_STICKY;
106 }
107
108 @Override
109 public void onCreate() {
110 databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
111 this.accounts = databaseBackend.getAccounts();
112 }
113
114 public XmppConnection createConnection(Account account) {
115 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
116 XmppConnection connection = new XmppConnection(account, pm);
117 connection
118 .setOnMessagePacketReceivedListener(this.messageListener);
119 connection.setOnStatusChangedListener(this.statusListener );
120 Thread thread = new Thread(connection);
121 thread.start();
122 return connection;
123 }
124
125 @Override
126 public IBinder onBind(Intent intent) {
127 return mBinder;
128 }
129
130 public void sendMessage(final Account account, final Message message) {
131 Log.d(LOGTAG, "sending message for " + account.getJid() + " to: "
132 + message.getCounterpart());
133 databaseBackend.createMessage(message);
134 MessagePacket packet = new MessagePacket();
135 packet.setType(MessagePacket.TYPE_CHAT);
136 packet.setTo(message.getCounterpart());
137 packet.setFrom(account.getJid());
138 packet.setBody(message.getBody());
139 connections.get(account).sendMessagePacket(packet);
140 message.setStatus(Message.STATUS_SEND);
141 databaseBackend.updateMessage(message);
142 }
143
144 public void getRoster(final Account account,
145 final OnRosterFetchedListener listener) {
146 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
147 Element query = new Element("query");
148 query.setAttribute("xmlns", "jabber:iq:roster");
149 query.setAttribute("ver", "");
150 iqPacket.addChild(query);
151 connections.get(account).sendIqPacket(iqPacket,
152 new OnIqPacketReceived() {
153
154 @Override
155 public void onIqPacketReceived(Account account,
156 IqPacket packet) {
157 Element roster = packet.findChild("query");
158 List<Contact> contacts = new ArrayList<Contact>();
159 for (Element item : roster.getChildren()) {
160 String name = item.getAttribute("name");
161 String jid = item.getAttribute("jid");
162 if (name == null) {
163 name = jid.split("@")[0];
164 }
165 Contact contact = new Contact(account, name, jid,
166 null);
167 contacts.add(contact);
168 }
169 if (listener != null) {
170 listener.onRosterFetched(contacts);
171 }
172 }
173 });
174 }
175
176 public void addConversation(Conversation conversation) {
177 databaseBackend.createConversation(conversation);
178 }
179
180 public List<Conversation> getConversations() {
181 if (this.conversations == null) {
182 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
183 for (Account account : this.accounts) {
184 accountLookupTable.put(account.getUuid(), account);
185 }
186 this.conversations = databaseBackend
187 .getConversations(Conversation.STATUS_AVAILABLE);
188 for (Conversation conv : this.conversations) {
189 conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
190 }
191 }
192 return this.conversations;
193 }
194
195 public List<Account> getAccounts() {
196 return this.accounts;
197 }
198
199 public List<Message> getMessages(Conversation conversation) {
200 return databaseBackend.getMessages(conversation, 100);
201 }
202
203 public Conversation findOrCreateConversation(Account account,
204 Contact contact) {
205 // Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
206 for (Conversation conv : this.getConversations()) {
207 if ((conv.getAccount().equals(account))
208 && (conv.getContactJid().equals(contact.getJid()))) {
209 // Log.d(LOGTAG,"found one in memory");
210 return conv;
211 }
212 }
213 Conversation conversation = databaseBackend.findConversation(account,
214 contact.getJid());
215 if (conversation != null) {
216 Log.d("gultsch", "found one. unarchive it");
217 conversation.setStatus(Conversation.STATUS_AVAILABLE);
218 conversation.setAccount(account);
219 this.databaseBackend.updateConversation(conversation);
220 } else {
221 Log.d(LOGTAG, "didnt find one in archive. create new one");
222 conversation = new Conversation(contact.getDisplayName(),
223 contact.getProfilePhoto(), account, contact.getJid());
224 this.databaseBackend.createConversation(conversation);
225 }
226 this.conversations.add(conversation);
227 if (this.convChangedListener != null) {
228 this.convChangedListener.onConversationListChanged();
229 }
230 return conversation;
231 }
232
233 public void archiveConversation(Conversation conversation) {
234 this.databaseBackend.updateConversation(conversation);
235 this.conversations.remove(conversation);
236 if (this.convChangedListener != null) {
237 this.convChangedListener.onConversationListChanged();
238 }
239 }
240
241 public int getConversationCount() {
242 return this.databaseBackend.getConversationCount();
243 }
244
245 public void createAccount(Account account) {
246 databaseBackend.createAccount(account);
247 this.accounts.add(account);
248 this.connections.put(account, this.createConnection(account));
249 if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
250 }
251
252 public void updateAccount(Account account) {
253 databaseBackend.updateAccount(account);
254 XmppConnection connection = this.connections.get(account);
255 if (connection != null) {
256 connection.disconnect();
257 this.connections.remove(account);
258 }
259 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
260 this.connections.put(account, this.createConnection(account));
261 } else {
262 Log.d(LOGTAG,account.getJid()+": not starting because it's disabled");
263 }
264 if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
265 }
266
267 public void deleteAccount(Account account) {
268 Log.d(LOGTAG,"called delete account");
269 if (this.connections.containsKey(account)) {
270 Log.d(LOGTAG,"found connection. disconnecting");
271 this.connections.get(account).disconnect();
272 this.connections.remove(account);
273 this.accounts.remove(account);
274 }
275 databaseBackend.deleteAccount(account);
276 if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
277 }
278
279 public void setOnConversationListChangedListener(
280 OnConversationListChangedListener listener) {
281 this.convChangedListener = listener;
282 }
283
284 public void removeOnConversationListChangedListener() {
285 this.convChangedListener = null;
286 }
287
288 public void setOnAccountListChangedListener(OnAccountListChangedListener listener) {
289 this.accountChangedListener = listener;
290 }
291
292 public void removeOnAccountListChangedListener() {
293 this.accountChangedListener = null;
294 }
295}