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