| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | package de.gultsch.chat.services;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-02 16:05:15 +01:00
										 |  |  | import java.util.ArrayList;
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | import java.util.Hashtable;
 | 
					
						
							| 
									
										
										
										
											2014-01-25 19:33:12 +01:00
										 |  |  | import java.util.List;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-27 20:40:42 +01:00
										 |  |  | import de.gultsch.chat.entities.Account;
 | 
					
						
							|  |  |  | import de.gultsch.chat.entities.Contact;
 | 
					
						
							| 
									
										
										
										
											2014-01-25 19:33:12 +01:00
										 |  |  | import de.gultsch.chat.entities.Conversation;
 | 
					
						
							|  |  |  | import de.gultsch.chat.entities.Message;
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | import de.gultsch.chat.entities.Presences;
 | 
					
						
							| 
									
										
										
										
											2014-01-25 19:33:12 +01:00
										 |  |  | import de.gultsch.chat.persistance.DatabaseBackend;
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | import de.gultsch.chat.ui.OnAccountListChangedListener;
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | import de.gultsch.chat.ui.OnConversationListChangedListener;
 | 
					
						
							| 
									
										
										
										
											2014-02-02 16:05:15 +01:00
										 |  |  | import de.gultsch.chat.ui.OnRosterFetchedListener;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | import de.gultsch.chat.utils.UIHelper;
 | 
					
						
							| 
									
										
										
										
											2014-02-02 16:05:15 +01:00
										 |  |  | import de.gultsch.chat.xml.Element;
 | 
					
						
							|  |  |  | import de.gultsch.chat.xmpp.IqPacket;
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | import de.gultsch.chat.xmpp.MessagePacket;
 | 
					
						
							| 
									
										
										
										
											2014-02-02 16:05:15 +01:00
										 |  |  | import de.gultsch.chat.xmpp.OnIqPacketReceived;
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | import de.gultsch.chat.xmpp.OnMessagePacketReceived;
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | import de.gultsch.chat.xmpp.OnPresencePacketReceived;
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | import de.gultsch.chat.xmpp.OnStatusChanged;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | import de.gultsch.chat.xmpp.PresencePacket;
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | import de.gultsch.chat.xmpp.XmppConnection;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | import android.app.NotificationManager;
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | import android.app.Service;
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | import android.content.Context;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | import android.content.CursorLoader;
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | import android.content.Intent;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | import android.content.Loader;
 | 
					
						
							|  |  |  | import android.content.Loader.OnLoadCompleteListener;
 | 
					
						
							|  |  |  | import android.database.Cursor;
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | import android.os.Binder;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | import android.os.Bundle;
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | import android.os.IBinder;
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | import android.os.PowerManager;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | import android.provider.ContactsContract;
 | 
					
						
							| 
									
										
										
										
											2014-01-25 19:33:12 +01:00
										 |  |  | import android.util.Log;
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | public class XmppConnectionService extends Service {
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | 	protected static final String LOGTAG = "xmppService";
 | 
					
						
							| 
									
										
										
										
											2014-01-25 19:33:12 +01:00
										 |  |  | 	protected DatabaseBackend databaseBackend;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | 	public long startDate;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:42:35 +01:00
										 |  |  | 	private List<Account> accounts;
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 	private List<Conversation> conversations = null;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 	private OnConversationListChangedListener convChangedListener = null;
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 	private OnAccountListChangedListener accountChangedListener = null;
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	private final IBinder mBinder = new XmppConnectionBinder();
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 	private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 		@Override
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 		public void onMessagePacketReceived(Account account,
 | 
					
						
							|  |  |  | 				MessagePacket packet) {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 			if ((packet.getType() == MessagePacket.TYPE_CHAT)
 | 
					
						
							|  |  |  | 					|| (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
 | 
					
						
							|  |  |  | 				boolean notify = true;
 | 
					
						
							|  |  |  | 				if (!packet.hasChild("body")) {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 					return;
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 				Conversation conversation = null;
 | 
					
						
							|  |  |  | 				String fullJid = packet.getFrom();
 | 
					
						
							|  |  |  | 				String[] fromParts = fullJid.split("/");
 | 
					
						
							|  |  |  | 				String jid = fromParts[0];
 | 
					
						
							|  |  |  | 				Contact contact = findOrCreateContact(account, jid);
 | 
					
						
							|  |  |  | 				boolean muc = (packet.getType() == MessagePacket.TYPE_GROUPCHAT);
 | 
					
						
							|  |  |  | 				String counterPart = null;
 | 
					
						
							|  |  |  | 				int status = Message.STATUS_RECIEVED;
 | 
					
						
							|  |  |  | 				conversation = findOrCreateConversation(account, contact, muc);
 | 
					
						
							|  |  |  | 				if (muc) {
 | 
					
						
							|  |  |  | 					if ((fromParts.length==1)||(packet.hasChild("subject"))||(packet.hasChild("delay"))) {
 | 
					
						
							|  |  |  | 						return;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 					counterPart = fromParts[1];
 | 
					
						
							|  |  |  | 					if (counterPart.equals(account.getUsername())) {
 | 
					
						
							|  |  |  | 						status = Message.STATUS_SEND;
 | 
					
						
							|  |  |  | 						notify = false;
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 				} else {
 | 
					
						
							|  |  |  | 					counterPart = fullJid;
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 				}
 | 
					
						
							|  |  |  | 				Message message = new Message(conversation, counterPart,
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 						packet.getBody(), Message.ENCRYPTION_NONE, status);
 | 
					
						
							| 
									
										
										
										
											2014-02-02 17:53:34 +01:00
										 |  |  | 				conversation.getMessages().add(message);
 | 
					
						
							|  |  |  | 				databaseBackend.createMessage(message);
 | 
					
						
							|  |  |  | 				if (convChangedListener != null) {
 | 
					
						
							|  |  |  | 					convChangedListener.onConversationListChanged();
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 				} else {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 					if (notify) {
 | 
					
						
							|  |  |  | 						NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 | 
					
						
							|  |  |  | 						mNotificationManager.notify(2342, UIHelper
 | 
					
						
							|  |  |  | 								.getUnreadMessageNotification(
 | 
					
						
							|  |  |  | 										getApplicationContext(), conversation));
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2014-02-02 17:53:34 +01:00
										 |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	};
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 	private OnStatusChanged statusListener = new OnStatusChanged() {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		@Override
 | 
					
						
							|  |  |  | 		public void onStatusChanged(Account account) {
 | 
					
						
							|  |  |  | 			if (accountChangedListener != null) {
 | 
					
						
							|  |  |  | 				accountChangedListener.onAccountListChangedListener();
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 			if (account.getStatus() == Account.STATUS_ONLINE) {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 				databaseBackend.clearPresences(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 				connectMultiModeConversations(account);
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	};
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 	private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 		@Override
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		public void onPresencePacketReceived(Account account,
 | 
					
						
							|  |  |  | 				PresencePacket packet) {
 | 
					
						
							|  |  |  | 			String[] fromParts = packet.getAttribute("from").split("/");
 | 
					
						
							|  |  |  | 			Contact contact = findOrCreateContact(account, fromParts[0]);
 | 
					
						
							|  |  |  | 			if (contact.getUuid()==null) {
 | 
					
						
							|  |  |  | 				//most likely muc, self or roster not synced
 | 
					
						
							|  |  |  | 				Log.d(LOGTAG,"got presence for non contact "+packet.toString());
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 			String type = packet.getAttribute("type");
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 			if (type == null) {
 | 
					
						
							|  |  |  | 				Element show = packet.findChild("show");
 | 
					
						
							|  |  |  | 				if (show==null) {
 | 
					
						
							|  |  |  | 					contact.updatePresence(fromParts[1],Presences.ONLINE);
 | 
					
						
							|  |  |  | 				} else if (show.getContent().equals("away")) {
 | 
					
						
							|  |  |  | 					contact.updatePresence(fromParts[1],Presences.AWAY);
 | 
					
						
							|  |  |  | 				} else if (show.getContent().equals("xa")) {
 | 
					
						
							|  |  |  | 					contact.updatePresence(fromParts[1],Presences.XA);
 | 
					
						
							|  |  |  | 				} else if (show.getContent().equals("chat")) {
 | 
					
						
							|  |  |  | 					contact.updatePresence(fromParts[1],Presences.CHAT);
 | 
					
						
							|  |  |  | 				} else if (show.getContent().equals("dnd")) {
 | 
					
						
							|  |  |  | 					contact.updatePresence(fromParts[1],Presences.DND);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				databaseBackend.updateContact(contact);
 | 
					
						
							|  |  |  | 			} else if (type.equals("unavailable")) {
 | 
					
						
							|  |  |  | 				if (fromParts.length!=2) {
 | 
					
						
							|  |  |  | 					Log.d(LOGTAG,"received presence with no resource "+packet.toString());
 | 
					
						
							|  |  |  | 				} else {
 | 
					
						
							|  |  |  | 					contact.removePresence(fromParts[1]);
 | 
					
						
							|  |  |  | 					databaseBackend.updateContact(contact);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	};
 | 
					
						
							| 
									
										
										
										
											2014-01-24 02:04:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 	public class XmppConnectionBinder extends Binder {
 | 
					
						
							|  |  |  | 		public XmppConnectionService getService() {
 | 
					
						
							|  |  |  | 			return XmppConnectionService.this;
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	@Override
 | 
					
						
							|  |  |  | 	public int onStartCommand(Intent intent, int flags, int startId) {
 | 
					
						
							|  |  |  | 		for (Account account : accounts) {
 | 
					
						
							|  |  |  | 			if (!connections.containsKey(account)) {
 | 
					
						
							| 
									
										
										
										
											2014-02-04 21:44:16 +01:00
										 |  |  | 				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 					this.connections.put(account,
 | 
					
						
							|  |  |  | 							this.createConnection(account));
 | 
					
						
							| 
									
										
										
										
											2014-02-04 21:44:16 +01:00
										 |  |  | 				} else {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 					Log.d(LOGTAG, account.getJid()
 | 
					
						
							|  |  |  | 							+ ": not starting because it's disabled");
 | 
					
						
							| 
									
										
										
										
											2014-02-04 21:44:16 +01:00
										 |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return START_STICKY;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	@Override
 | 
					
						
							|  |  |  | 	public void onCreate() {
 | 
					
						
							|  |  |  | 		databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
 | 
					
						
							|  |  |  | 		this.accounts = databaseBackend.getAccounts();
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 	public XmppConnection createConnection(Account account) {
 | 
					
						
							|  |  |  | 		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 | 
					
						
							|  |  |  | 		XmppConnection connection = new XmppConnection(account, pm);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		connection.setOnMessagePacketReceivedListener(this.messageListener);
 | 
					
						
							|  |  |  | 		connection.setOnStatusChangedListener(this.statusListener);
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 		connection.setOnPresencePacketReceivedListener(this.presenceListener);
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		Thread thread = new Thread(connection);
 | 
					
						
							|  |  |  | 		thread.start();
 | 
					
						
							|  |  |  | 		return connection;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void sendMessage(final Account account, final Message message) {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 01:00:23 +01:00
										 |  |  | 		if (message.getConversation().getMode()==Conversation.MODE_SINGLE) {
 | 
					
						
							|  |  |  | 			databaseBackend.createMessage(message);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		MessagePacket packet = new MessagePacket();
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
 | 
					
						
							|  |  |  | 			packet.setType(MessagePacket.TYPE_CHAT);
 | 
					
						
							|  |  |  | 		} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
 | 
					
						
							|  |  |  | 			packet.setType(MessagePacket.TYPE_GROUPCHAT);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		packet.setTo(message.getCounterpart());
 | 
					
						
							|  |  |  | 		packet.setFrom(account.getJid());
 | 
					
						
							|  |  |  | 		packet.setBody(message.getBody());
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		if (account.getStatus()==Account.STATUS_ONLINE) {
 | 
					
						
							|  |  |  | 			connections.get(account).sendMessagePacket(packet);
 | 
					
						
							| 
									
										
										
										
											2014-02-09 01:00:23 +01:00
										 |  |  | 			if (message.getConversation().getMode()==Conversation.MODE_SINGLE) {
 | 
					
						
							|  |  |  | 				message.setStatus(Message.STATUS_SEND);
 | 
					
						
							|  |  |  | 				databaseBackend.updateMessage(message);
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void getRoster(Account account,
 | 
					
						
							|  |  |  | 			final OnRosterFetchedListener listener) {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 02:57:36 +01:00
										 |  |  | 		List<Contact> contacts = databaseBackend.getContacts(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		for (int i = 0; i < contacts.size(); ++i) {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 			contacts.get(i).setAccount(account);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		if (listener != null) {
 | 
					
						
							|  |  |  | 			listener.onRosterFetched(contacts);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 	public void updateRoster(final Account account,
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 			final OnRosterFetchedListener listener) {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		final Hashtable<String, Bundle> phoneContacts = new Hashtable<String, Bundle>();
 | 
					
						
							|  |  |  | 		final List<Contact> contacts = new ArrayList<Contact>();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		final String[] PROJECTION = new String[] {
 | 
					
						
							|  |  |  | 				ContactsContract.Data.CONTACT_ID,
 | 
					
						
							|  |  |  | 				ContactsContract.Data.DISPLAY_NAME,
 | 
					
						
							|  |  |  | 				ContactsContract.Data.PHOTO_THUMBNAIL_URI,
 | 
					
						
							|  |  |  | 				ContactsContract.CommonDataKinds.Im.DATA };
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
 | 
					
						
							|  |  |  | 				+ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
 | 
					
						
							|  |  |  | 				+ "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
 | 
					
						
							|  |  |  | 				+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
 | 
					
						
							|  |  |  | 				+ "\")";
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		CursorLoader mCursorLoader = new CursorLoader(this,
 | 
					
						
							|  |  |  | 				ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
 | 
					
						
							|  |  |  | 				null);
 | 
					
						
							|  |  |  | 		mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			@Override
 | 
					
						
							|  |  |  | 			public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
 | 
					
						
							|  |  |  | 				while (cursor.moveToNext()) {
 | 
					
						
							|  |  |  | 					Bundle contact = new Bundle();
 | 
					
						
							|  |  |  | 					contact.putInt("phoneid", cursor.getInt(cursor
 | 
					
						
							|  |  |  | 							.getColumnIndex(ContactsContract.Data.CONTACT_ID)));
 | 
					
						
							|  |  |  | 					contact.putString(
 | 
					
						
							|  |  |  | 							"displayname",
 | 
					
						
							|  |  |  | 							cursor.getString(cursor
 | 
					
						
							|  |  |  | 									.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
 | 
					
						
							|  |  |  | 					contact.putString(
 | 
					
						
							|  |  |  | 							"photouri",
 | 
					
						
							|  |  |  | 							cursor.getString(cursor
 | 
					
						
							|  |  |  | 									.getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI)));
 | 
					
						
							|  |  |  | 					phoneContacts.put(
 | 
					
						
							|  |  |  | 							cursor.getString(cursor
 | 
					
						
							|  |  |  | 									.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)),
 | 
					
						
							|  |  |  | 							contact);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
 | 
					
						
							|  |  |  | 				Element query = new Element("query");
 | 
					
						
							|  |  |  | 				query.setAttribute("xmlns", "jabber:iq:roster");
 | 
					
						
							|  |  |  | 				query.setAttribute("ver", "");
 | 
					
						
							|  |  |  | 				iqPacket.addChild(query);
 | 
					
						
							|  |  |  | 				connections.get(account).sendIqPacket(iqPacket,
 | 
					
						
							|  |  |  | 						new OnIqPacketReceived() {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							@Override
 | 
					
						
							|  |  |  | 							public void onIqPacketReceived(Account account,
 | 
					
						
							|  |  |  | 									IqPacket packet) {
 | 
					
						
							|  |  |  | 								Element roster = packet.findChild("query");
 | 
					
						
							|  |  |  | 								if (roster != null) {
 | 
					
						
							|  |  |  | 									for (Element item : roster.getChildren()) {
 | 
					
						
							|  |  |  | 										Contact contact;
 | 
					
						
							|  |  |  | 										String name = item.getAttribute("name");
 | 
					
						
							|  |  |  | 										String jid = item.getAttribute("jid");
 | 
					
						
							|  |  |  | 										if (phoneContacts.containsKey(jid)) {
 | 
					
						
							|  |  |  | 											Bundle phoneContact = phoneContacts
 | 
					
						
							|  |  |  | 													.get(jid);
 | 
					
						
							|  |  |  | 											contact = new Contact(
 | 
					
						
							|  |  |  | 													account,
 | 
					
						
							|  |  |  | 													phoneContact
 | 
					
						
							|  |  |  | 															.getString("displayname"),
 | 
					
						
							|  |  |  | 													jid,
 | 
					
						
							|  |  |  | 													phoneContact
 | 
					
						
							|  |  |  | 															.getString("photouri"));
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 											contact.setSystemAccount(phoneContact
 | 
					
						
							|  |  |  | 													.getInt("phoneid"));
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 										} else {
 | 
					
						
							|  |  |  | 											if (name == null) {
 | 
					
						
							|  |  |  | 												name = jid.split("@")[0];
 | 
					
						
							|  |  |  | 											}
 | 
					
						
							|  |  |  | 											contact = new Contact(account,
 | 
					
						
							|  |  |  | 													name, jid, null);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 										}
 | 
					
						
							|  |  |  | 										contact.setAccount(account);
 | 
					
						
							|  |  |  | 										contact.setSubscription(item
 | 
					
						
							|  |  |  | 												.getAttribute("subscription"));
 | 
					
						
							|  |  |  | 										contacts.add(contact);
 | 
					
						
							|  |  |  | 									}
 | 
					
						
							|  |  |  | 									databaseBackend.mergeContacts(contacts);
 | 
					
						
							|  |  |  | 									if (listener != null) {
 | 
					
						
							|  |  |  | 										listener.onRosterFetched(contacts);
 | 
					
						
							|  |  |  | 									}
 | 
					
						
							|  |  |  | 								}
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 							}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 						});
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		});
 | 
					
						
							|  |  |  | 		mCursorLoader.startLoading();
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public void addConversation(Conversation conversation) {
 | 
					
						
							|  |  |  | 		databaseBackend.createConversation(conversation);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public List<Conversation> getConversations() {
 | 
					
						
							|  |  |  | 		if (this.conversations == null) {
 | 
					
						
							|  |  |  | 			Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
 | 
					
						
							|  |  |  | 			for (Account account : this.accounts) {
 | 
					
						
							|  |  |  | 				accountLookupTable.put(account.getUuid(), account);
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			this.conversations = databaseBackend
 | 
					
						
							|  |  |  | 					.getConversations(Conversation.STATUS_AVAILABLE);
 | 
					
						
							|  |  |  | 			for (Conversation conv : this.conversations) {
 | 
					
						
							|  |  |  | 				conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return this.conversations;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public List<Account> getAccounts() {
 | 
					
						
							|  |  |  | 		return this.accounts;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public List<Message> getMessages(Conversation conversation) {
 | 
					
						
							|  |  |  | 		return databaseBackend.getMessages(conversation, 100);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 	public Contact findOrCreateContact(Account account, String jid) {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		Contact contact = databaseBackend.findContact(account, jid);
 | 
					
						
							|  |  |  | 		if (contact != null) {
 | 
					
						
							|  |  |  | 			contact.setAccount(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 			return contact;
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 			return new Contact(account, jid.split("@")[0], jid, null);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public Conversation findOrCreateConversation(Account account,
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 			Contact contact, boolean muc) {
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 		for (Conversation conv : this.getConversations()) {
 | 
					
						
							|  |  |  | 			if ((conv.getAccount().equals(account))
 | 
					
						
							|  |  |  | 					&& (conv.getContactJid().equals(contact.getJid()))) {
 | 
					
						
							|  |  |  | 				return conv;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		Conversation conversation = databaseBackend.findConversation(account,
 | 
					
						
							|  |  |  | 				contact.getJid());
 | 
					
						
							|  |  |  | 		if (conversation != null) {
 | 
					
						
							|  |  |  | 			conversation.setStatus(Conversation.STATUS_AVAILABLE);
 | 
					
						
							|  |  |  | 			conversation.setAccount(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 			if (muc) {
 | 
					
						
							|  |  |  | 				conversation.setMode(Conversation.MODE_MULTI);
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 				if (account.getStatus() == Account.STATUS_ONLINE) {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 					joinMuc(account, conversation);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			} else {
 | 
					
						
							|  |  |  | 				conversation.setMode(Conversation.MODE_SINGLE);
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 			this.databaseBackend.updateConversation(conversation);
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 			if (muc) {
 | 
					
						
							|  |  |  | 				conversation = new Conversation(contact.getDisplayName(),
 | 
					
						
							|  |  |  | 						contact.getProfilePhoto(), account, contact.getJid(),
 | 
					
						
							|  |  |  | 						Conversation.MODE_MULTI);
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 				if (account.getStatus() == Account.STATUS_ONLINE) {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 					joinMuc(account, conversation);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			} else {
 | 
					
						
							|  |  |  | 				conversation = new Conversation(contact.getDisplayName(),
 | 
					
						
							|  |  |  | 						contact.getProfilePhoto(), account, contact.getJid(),
 | 
					
						
							|  |  |  | 						Conversation.MODE_SINGLE);
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 			this.databaseBackend.createConversation(conversation);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		this.conversations.add(conversation);
 | 
					
						
							|  |  |  | 		if (this.convChangedListener != null) {
 | 
					
						
							|  |  |  | 			this.convChangedListener.onConversationListChanged();
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return conversation;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public void archiveConversation(Conversation conversation) {
 | 
					
						
							|  |  |  | 		this.databaseBackend.updateConversation(conversation);
 | 
					
						
							|  |  |  | 		this.conversations.remove(conversation);
 | 
					
						
							|  |  |  | 		if (this.convChangedListener != null) {
 | 
					
						
							|  |  |  | 			this.convChangedListener.onConversationListChanged();
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public int getConversationCount() {
 | 
					
						
							|  |  |  | 		return this.databaseBackend.getConversationCount();
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void createAccount(Account account) {
 | 
					
						
							|  |  |  | 		databaseBackend.createAccount(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		this.accounts.add(account);
 | 
					
						
							|  |  |  | 		this.connections.put(account, this.createConnection(account));
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		if (accountChangedListener != null)
 | 
					
						
							|  |  |  | 			accountChangedListener.onAccountListChangedListener();
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 	public void updateAccount(Account account) {
 | 
					
						
							|  |  |  | 		databaseBackend.updateAccount(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		XmppConnection connection = this.connections.get(account);
 | 
					
						
							|  |  |  | 		if (connection != null) {
 | 
					
						
							|  |  |  | 			connection.disconnect();
 | 
					
						
							|  |  |  | 			this.connections.remove(account);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-04 21:44:16 +01:00
										 |  |  | 		if (!account.isOptionSet(Account.OPTION_DISABLED)) {
 | 
					
						
							|  |  |  | 			this.connections.put(account, this.createConnection(account));
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 			Log.d(LOGTAG, account.getJid()
 | 
					
						
							|  |  |  | 					+ ": not starting because it's disabled");
 | 
					
						
							| 
									
										
										
										
											2014-02-04 21:44:16 +01:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		if (accountChangedListener != null)
 | 
					
						
							|  |  |  | 			accountChangedListener.onAccountListChangedListener();
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public void deleteAccount(Account account) {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		Log.d(LOGTAG, "called delete account");
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		if (this.connections.containsKey(account)) {
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 			Log.d(LOGTAG, "found connection. disconnecting");
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 			this.connections.get(account).disconnect();
 | 
					
						
							|  |  |  | 			this.connections.remove(account);
 | 
					
						
							|  |  |  | 			this.accounts.remove(account);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 		databaseBackend.deleteAccount(account);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 		if (accountChangedListener != null)
 | 
					
						
							|  |  |  | 			accountChangedListener.onAccountListChangedListener();
 | 
					
						
							| 
									
										
										
										
											2014-01-28 19:21:54 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void setOnConversationListChangedListener(
 | 
					
						
							|  |  |  | 			OnConversationListChangedListener listener) {
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 		this.convChangedListener = listener;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-03 18:38:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 15:07:20 +01:00
										 |  |  | 	public void removeOnConversationListChangedListener() {
 | 
					
						
							|  |  |  | 		this.convChangedListener = null;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void setOnAccountListChangedListener(
 | 
					
						
							|  |  |  | 			OnAccountListChangedListener listener) {
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 		this.accountChangedListener = listener;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-04 15:09:50 +01:00
										 |  |  | 	public void removeOnAccountListChangedListener() {
 | 
					
						
							|  |  |  | 		this.accountChangedListener = null;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void connectMultiModeConversations(Account account) {
 | 
					
						
							|  |  |  | 		List<Conversation> conversations = getConversations();
 | 
					
						
							|  |  |  | 		for (int i = 0; i < conversations.size(); i++) {
 | 
					
						
							|  |  |  | 			Conversation conversation = conversations.get(i);
 | 
					
						
							|  |  |  | 			if ((conversation.getMode() == Conversation.MODE_MULTI)
 | 
					
						
							|  |  |  | 					&& (conversation.getAccount() == account)) {
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 				joinMuc(account, conversation);
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 	public void joinMuc(Account account, Conversation conversation) {
 | 
					
						
							|  |  |  | 		String muc = conversation.getContactJid();
 | 
					
						
							|  |  |  | 		PresencePacket packet = new PresencePacket();
 | 
					
						
							|  |  |  | 		packet.setAttribute("to", muc + "/" + account.getUsername());
 | 
					
						
							|  |  |  | 		Element x = new Element("x");
 | 
					
						
							|  |  |  | 		x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
 | 
					
						
							|  |  |  | 		packet.addChild(x);
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 		connections.get(conversation.getAccount()).sendPresencePacket(packet);
 | 
					
						
							| 
									
										
										
										
											2014-02-07 16:50:29 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-05 22:33:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public void disconnectMultiModeConversations() {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-02-09 00:47:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	@Override
 | 
					
						
							|  |  |  | 	public IBinder onBind(Intent intent) {
 | 
					
						
							|  |  |  | 		return mBinder;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 |