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 String fullJid = packet.getFrom();
51 String jid = fullJid.split("/")[0];
52 String name = jid.split("@")[0];
53 Contact contact = new Contact(account,name,jid,null); //dummy contact
54 Conversation conversation = findOrCreateConversation(account, contact);
55 Message message = new Message(conversation, fullJid, packet.getBody(), Message.ENCRYPTION_NONE, Message.STATUS_RECIEVED);
56 conversation.getMessages().add(message);
57 databaseBackend.createMessage(message);
58 if (convChangedListener != null) {
59 convChangedListener.onConversationListChanged();
60 }
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 new Thread() {
121 @Override
122 public void run() {
123 IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
124 Element query = new Element("query");
125 query.setAttribute("xmlns", "jabber:iq:roster");
126 query.setAttribute("ver", "");
127 iqPacket.addChild(query);
128 try {
129 connections.get(account).sendIqPacket(iqPacket, new OnIqPacketReceived() {
130
131 @Override
132 public void onIqPacketReceived(Account account, IqPacket packet) {
133 Element roster = packet.findChild("query");
134 List<Contact> contacts = new ArrayList<Contact>();
135 for(Element item : roster.getChildren()) {
136 String name = item.getAttribute("name");
137 String jid = item.getAttribute("jid");
138 if (name==null) {
139 name = jid.split("@")[0];
140 }
141 Contact contact = new Contact(account, name, jid, null);
142 contacts.add(contact);
143 }
144 if (listener != null) {
145 listener.onRosterFetched(contacts);
146 }
147 }
148 });
149 } catch (IOException e) {
150 Log.d(LOGTAG,"io error during roster fetch");
151 }
152 }
153 }.start();
154 }
155
156 public void addConversation(Conversation conversation) {
157 databaseBackend.createConversation(conversation);
158 }
159
160 public List<Conversation> getConversations() {
161 if (this.conversations == null) {
162 Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
163 for(Account account : this.accounts) {
164 accountLookupTable.put(account.getUuid(), account);
165 }
166 this.conversations = databaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
167 for(Conversation conv : this.conversations) {
168 conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
169 }
170 }
171 return this.conversations;
172 }
173
174 public List<Account> getAccounts() {
175 return this.accounts;
176 }
177
178 public List<Message> getMessages(Conversation conversation) {
179 return databaseBackend.getMessages(conversation, 100);
180 }
181
182 public Conversation findOrCreateConversation(Account account, Contact contact) {
183 //Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
184 for(Conversation conv : this.getConversations()) {
185 if ((conv.getAccount().equals(account))&&(conv.getContactJid().equals(contact.getJid()))) {
186 //Log.d(LOGTAG,"found one in memory");
187 return conv;
188 }
189 }
190 Conversation conversation = databaseBackend.findConversation(account, contact.getJid());
191 if (conversation!=null) {
192 Log.d("gultsch","found one. unarchive it");
193 conversation.setStatus(Conversation.STATUS_AVAILABLE);
194 conversation.setAccount(account);
195 this.databaseBackend.updateConversation(conversation);
196 } else {
197 Log.d(LOGTAG,"didnt find one in archive. create new one");
198 conversation = new Conversation(contact.getDisplayName(), contact.getProfilePhoto(), account, contact.getJid());
199 this.databaseBackend.createConversation(conversation);
200 }
201 this.conversations.add(conversation);
202 if (this.convChangedListener != null) {
203 this.convChangedListener.onConversationListChanged();
204 }
205 return conversation;
206 }
207
208 public void archiveConversation(Conversation conversation) {
209 this.databaseBackend.updateConversation(conversation);
210 this.conversations.remove(conversation);
211 if (this.convChangedListener != null) {
212 this.convChangedListener.onConversationListChanged();
213 }
214 }
215
216 public int getConversationCount() {
217 return this.databaseBackend.getConversationCount();
218 }
219
220 public void createAccount(Account account) {
221 databaseBackend.createAccount(account);
222 }
223
224 public void updateAccount(Account account) {
225 databaseBackend.updateAccount(account);
226 }
227
228 public void deleteAccount(Account account) {
229 databaseBackend.deleteAccount(account);
230 }
231
232 public void setOnConversationListChangedListener(OnConversationListChangedListener listener) {
233 this.convChangedListener = listener;
234 }
235
236 public void removeOnConversationListChangedListener() {
237 this.convChangedListener = null;
238 }
239}