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 if (packet.getType()==MessagePacket.TYPE_CHAT) {
50 Log.d(LOGTAG,account.getJid()+": message of type chat");
51 String fullJid = packet.getFrom();
52 String jid = fullJid.split("/")[0];
53 String name = jid.split("@")[0];
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
66 public class XmppConnectionBinder extends Binder {
67 public XmppConnectionService getService() {
68 return XmppConnectionService.this;
69 }
70 }
71
72 @Override
73 public int onStartCommand(Intent intent, int flags, int startId) {
74 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
75 for(Account account : accounts) {
76 if (!connections.containsKey(account)) {
77 XmppConnection connection = new XmppConnection(account, pm);
78 connection.setOnMessagePacketReceivedListener(this.messageListener );
79 Thread thread = new Thread(connection);
80 thread.start();
81 this.connections.put(account, connection);
82 }
83 }
84 return START_STICKY;
85 }
86
87 @Override
88 public void onCreate() {
89 databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
90 this.accounts = databaseBackend.getAccounts();
91 }
92
93 @Override
94 public IBinder onBind(Intent intent) {
95 return mBinder;
96 }
97
98 public void sendMessage(final Account account, final Message message) {
99 new Thread() {
100 @Override
101 public void run() {
102 Log.d(LOGTAG,"sending message for "+account.getJid()+" to: "+message.getCounterpart());
103 databaseBackend.createMessage(message);
104 MessagePacket packet = new MessagePacket();
105 packet.setType(MessagePacket.TYPE_CHAT);
106 packet.setTo(message.getCounterpart());
107 packet.setFrom(account.getJid());
108 packet.setBody(message.getBody());
109 try {
110 connections.get(account).sendMessagePacket(packet);
111 message.setStatus(Message.STATUS_SEND);
112 databaseBackend.updateMessage(message);
113 } catch (IOException e) {
114 Log.d(LOGTAG,"io exception during send. message is in database. will try again later");
115 }
116 }
117 }.start();
118 }
119
120 public void getRoster(final Account account, final OnRosterFetchedListener listener) {
121 new Thread() {
122 @Override
123 public void run() {
124 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
125 Element query = new Element("query");
126 query.setAttribute("xmlns", "jabber:iq:roster");
127 query.setAttribute("ver", "");
128 iqPacket.addChild(query);
129 try {
130 connections.get(account).sendIqPacket(iqPacket, new OnIqPacketReceived() {
131
132 @Override
133 public void onIqPacketReceived(Account account, IqPacket packet) {
134 Element roster = packet.findChild("query");
135 Log.d(LOGTAG,roster.toString());
136 List<Contact> contacts = new ArrayList<Contact>();
137 for(Element item : roster.getChildren()) {
138 String name = item.getAttribute("name");
139 String jid = item.getAttribute("jid");
140 if (name==null) {
141 name = jid.split("@")[0];
142 }
143 Contact contact = new Contact(account, name, jid, null);
144 contacts.add(contact);
145 }
146 if (listener != null) {
147 listener.onRosterFetched(contacts);
148 }
149 }
150 });
151 } catch (IOException e) {
152 Log.d(LOGTAG,"io error during roster fetch");
153 }
154 }
155 }.start();
156 }
157
158 public void addConversation(Conversation conversation) {
159 databaseBackend.createConversation(conversation);
160 }
161
162 public List<Conversation> getConversations() {
163 if (this.conversations == null) {
164 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
165 for(Account account : this.accounts) {
166 accountLookupTable.put(account.getUuid(), account);
167 }
168 this.conversations = databaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
169 for(Conversation conv : this.conversations) {
170 conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
171 }
172 }
173 return this.conversations;
174 }
175
176 public List<Account> getAccounts() {
177 return this.accounts;
178 }
179
180 public List<Message> getMessages(Conversation conversation) {
181 return databaseBackend.getMessages(conversation, 100);
182 }
183
184 public Conversation findOrCreateConversation(Account account, Contact contact) {
185 Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
186 for(Conversation conv : this.getConversations()) {
187 if ((conv.getAccount().equals(account))&&(conv.getContactJid().equals(contact.getJid()))) {
188 Log.d(LOGTAG,"found one in memory");
189 return conv;
190 }
191 }
192 Conversation conversation = databaseBackend.findConversation(account, contact.getJid());
193 if (conversation!=null) {
194 Log.d("gultsch","found one. unarchive it");
195 conversation.setStatus(Conversation.STATUS_AVAILABLE);
196 conversation.setAccount(account);
197 this.databaseBackend.updateConversation(conversation);
198 } else {
199 Log.d(LOGTAG,"didnt find one in archive. create new one");
200 conversation = new Conversation(contact.getDisplayName(), contact.getProfilePhoto(), account, contact.getJid());
201 this.databaseBackend.createConversation(conversation);
202 }
203 this.conversations.add(conversation);
204 if (this.convChangedListener != null) {
205 this.convChangedListener.onConversationListChanged();
206 }
207 return conversation;
208 }
209
210 public void archiveConversation(Conversation conversation) {
211 this.databaseBackend.updateConversation(conversation);
212 this.conversations.remove(conversation);
213 if (this.convChangedListener != null) {
214 this.convChangedListener.onConversationListChanged();
215 }
216 }
217
218 public int getConversationCount() {
219 return this.databaseBackend.getConversationCount();
220 }
221
222 public void createAccount(Account account) {
223 databaseBackend.createAccount(account);
224 }
225
226 public void updateAccount(Account account) {
227 databaseBackend.updateAccount(account);
228 }
229
230 public void deleteAccount(Account account) {
231 databaseBackend.deleteAccount(account);
232 }
233
234 public void setOnConversationListChangedListener(OnConversationListChangedListener listener) {
235 this.convChangedListener = listener;
236 }
237
238 public void removeOnConversationListChangedListener() {
239 this.convChangedListener = null;
240 }
241}