/*
BEEM is a videoconference application on the Android Platform.
Copyright (C) 2009 by Frederic-Charles Barthelery,
Jean-Manuel Da Silva,
Nikita Kozlov,
Philippe Lago,
Jean Baptiste Vergely,
Vincent Veronis.
This file is part of BEEM.
BEEM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
BEEM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with BEEM. If not, see <http://www.gnu.org/licenses/>.
Please send bug reports with examples or suggestions to
contact@beem-project.com or http://dev.beem-project.com/
Epitech, hereby disclaims all copyright interest in the program "Beem"
written by Frederic-Charles Barthelery,
Jean-Manuel Da Silva,
Nikita Kozlov,
Philippe Lago,
Jean Baptiste Vergely,
Vincent Veronis.
Nicolas Sadirac, November 26, 2009
President of Epitech.
Flavien Astraud, November 26, 2009
Head of the EIP Laboratory.
*/
package com.beem.project.beem.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import android.content.Context;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.beem.project.beem.R;
import com.beem.project.beem.service.aidl.IBeemRosterListener;
import com.beem.project.beem.utils.Status;
import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
import com.beem.project.beem.smack.avatar.AvatarManager;
import com.beem.project.beem.smack.avatar.AvatarListener;
/**
* This class implement a Roster adapter for BEEM.
*/
public class RosterAdapter extends com.beem.project.beem.service.aidl.IRoster.Stub {
private static final String TAG = "RosterAdapter";
private final Roster mAdaptee;
private final RemoteCallbackList<IBeemRosterListener> mRemoteRosListeners =
new RemoteCallbackList<IBeemRosterListener>();
private final Map<Integer, String> mDefaultStatusMessages;
private final RosterListenerAdapter mRosterListener = new RosterListenerAdapter();
private Map<String, String> mAvatarIdmap = new HashMap<String, String>();
private AvatarManager mAvatarManager;
/**
* Constructor.
* @param roster The roster to adapt.
* @param context The context of the RosterAdapter.
*/
public RosterAdapter(final Roster roster, final Context context) {
mAdaptee = roster;
roster.addRosterListener(mRosterListener);
mDefaultStatusMessages = createDefaultStatusMessagesMap(context);
}
/**
* Constructor.
* @param roster The roster to adapt.
* @param context The context of the RosterAdapter.
* @param avatarMgr The AvatarManager of the connection
*/
public RosterAdapter(final Roster roster, final Context context, final AvatarManager avatarMgr) {
mAdaptee = roster;
roster.addRosterListener(mRosterListener);
mDefaultStatusMessages = createDefaultStatusMessagesMap(context);
mAvatarManager = avatarMgr;
if (mAvatarManager != null)
mAvatarManager.addAvatarListener(new AvatarEventListener());
}
/**
* {@inheritDoc}
*/
@Override
public void addRosterListener(IBeemRosterListener listen) throws RemoteException {
if (listen != null)
mRemoteRosListeners.register(listen);
}
/**
* {@inheritDoc}
*/
@Override
public Contact addContact(String user, String name, String[] groups) throws RemoteException {
RosterEntry contact = mAdaptee.getEntry(user);
try {
mAdaptee.createEntry(user, name, groups);
contact = mAdaptee.getEntry(user);
} catch (XMPPException e) {
Log.e(TAG, "Error while adding new contact", e);
return null;
}
return getContactFromRosterEntry(contact);
}
/**
* {@inheritDoc}
*/
@Override
public void deleteContact(Contact contact) throws RemoteException {
try {
RosterEntry entry = mAdaptee.getEntry(contact.getJID());
mAdaptee.removeEntry(entry);
} catch (XMPPException e) {
e.printStackTrace();
}
}
/**
* {@inheritDoc}
*/
@Override
public void createGroup(String groupname) throws RemoteException {
if (mAdaptee.getGroup(groupname) == null)
mAdaptee.createGroup(groupname);
}
/**
* {@inheritDoc}
*/
@Override
public Contact getContact(String jid) throws RemoteException {
if (mAdaptee.contains(jid))
return getContactFromRosterEntry(mAdaptee.getEntry(jid));
return null;
}
/**
* {@inheritDoc}
*/
@Override
public List<Contact> getContactList() throws RemoteException {
boolean add = true;
Collection<RosterEntry> list = mAdaptee.getEntries();
List<Contact> coList = new ArrayList<Contact>(list.size());
for (RosterEntry entry : list) {
coList.add(getContactFromRosterEntry(entry));
}
return coList;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getGroupsNames() throws RemoteException {
Collection<RosterGroup> groups = mAdaptee.getGroups();
List<String> result = new ArrayList<String>(groups.size());
for (RosterGroup rosterGroup : groups) {
result.add(rosterGroup.getName());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void removeRosterListener(IBeemRosterListener listen) throws RemoteException {
if (listen != null)
mRemoteRosListeners.unregister(listen);
}
/**
* {@inheritDoc}
*/
@Override
public void setContactName(String jid, String name) throws RemoteException {
mAdaptee.getEntry(jid).setName(name);
}
/* (non-Javadoc)
* @see com.beem.project.beem.service.aidl.IRoster#getPresence(java.lang.String)
*/
@Override
public PresenceAdapter getPresence(String jid) throws RemoteException {
return new PresenceAdapter(mAdaptee.getPresence(jid));
}
/* (non-Javadoc)
* @see com.beem.project.beem.service.aidl.IRoster#addContactToGroup(java.lang.String, java.lang.String)
*/
@Override
public void addContactToGroup(String groupName, String jid) throws RemoteException {
createGroup(groupName);
RosterGroup group = mAdaptee.getGroup(groupName);
try {
group.addEntry(mAdaptee.getEntry(jid));
} catch (XMPPException e) {
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see com.beem.project.beem.service.aidl.IRoster#removeContactFromGroup(java.lang.String, java.lang.String)
*/
@Override
public void removeContactFromGroup(String groupName, String jid) throws RemoteException {
RosterGroup group = mAdaptee.getGroup(groupName);
try {
group.removeEntry(mAdaptee.getEntry(jid));
} catch (XMPPException e) {
e.printStackTrace();
}
}
/**
* Get a contact from a RosterEntry.
* @param entry a roster entry containing information for the contact.
* @return a contact for this entry.
*/
private Contact getContactFromRosterEntry(RosterEntry entry) {
String user = entry.getUser();
Contact c = new Contact(user);
Presence p = mAdaptee.getPresence(user);
if (p.getStatus() == null || "".equals(p.getStatus()))
p.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(p)));
c.setStatus(p);
try {
c.setGroups(entry.getGroups());
} catch (NullPointerException e) {
Log.d(TAG, "Group list not ready", e);
}
Iterator<Presence> iPres = mAdaptee.getPresences(user);
while (iPres.hasNext()) {
p = iPres.next();
if (!p.getType().equals(Presence.Type.unavailable))
c.addRes(StringUtils.parseResource(p.getFrom()));
}
c.setName(entry.getName());
c.setAvatarId(mAvatarIdmap.get(user));
return c;
}
/**
* Create a map which contains default status messages.
* @param context The context of the roster adapter.
* @return A Map<Integer, String> which assigns a status to a message.
*/
private Map<Integer, String> createDefaultStatusMessagesMap(Context context) {
Map<Integer, String> defaultStatusMessages = new HashMap<Integer, String>();
defaultStatusMessages.put(Status.CONTACT_STATUS_AVAILABLE, context
.getString(R.string.contact_status_msg_available));
defaultStatusMessages.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT, context
.getString(R.string.contact_status_msg_available_chat));
defaultStatusMessages.put(Status.CONTACT_STATUS_AWAY, context.getString(R.string.contact_status_msg_away));
defaultStatusMessages.put(Status.CONTACT_STATUS_BUSY, context.getString(R.string.contact_status_msg_dnd));
defaultStatusMessages.put(Status.CONTACT_STATUS_DISCONNECT, context
.getString(R.string.contact_status_msg_offline));
defaultStatusMessages.put(Status.CONTACT_STATUS_UNAVAILABLE, context.getString(R.string.contact_status_msg_xa));
return defaultStatusMessages;
}
/**
* Listener for the roster events. It will call the remote listeners registered.
* @author darisk
*/
private class RosterListenerAdapter implements RosterListener {
/**
* Constructor.
*/
public RosterListenerAdapter() {
}
/**
* {@inheritDoc}
*/
@Override
public void entriesAdded(Collection<String> addresses) {
final int n = mRemoteRosListeners.beginBroadcast();
List<String> tab = new ArrayList<String>();
tab.addAll(addresses);
for (int i = 0; i < n; i++) {
IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
try {
listener.onEntriesAdded(tab);
} catch (RemoteException e) {
Log.w(TAG, "Error while adding roster entries", e);
}
}
mRemoteRosListeners.finishBroadcast();
}
/**
* {@inheritDoc}
*/
@Override
public void entriesDeleted(Collection<String> addresses) {
final int n = mRemoteRosListeners.beginBroadcast();
List<String> tab = new ArrayList<String>();
tab.addAll(addresses);
for (int i = 0; i < n; i++) {
IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
try {
listener.onEntriesDeleted(tab);
} catch (RemoteException e) {
Log.w(TAG, "Error while deleting roster entries", e);
}
}
mRemoteRosListeners.finishBroadcast();
}
/**
* {@inheritDoc}
*/
@Override
public void entriesUpdated(Collection<String> addresses) {
final int n = mRemoteRosListeners.beginBroadcast();
List<String> tab = new ArrayList<String>();
tab.addAll(addresses);
for (int i = 0; i < n; i++) {
IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
try {
listener.onEntriesUpdated(tab);
} catch (RemoteException e) {
Log.w(TAG, "Error while updating roster entries", e);
}
}
mRemoteRosListeners.finishBroadcast();
}
/**
* {@inheritDoc}
*/
@Override
public void presenceChanged(Presence presence) {
final int n = mRemoteRosListeners.beginBroadcast();
Log.v(TAG, ">>> Presence changed for " + presence.getFrom());
for (int i = 0; i < n; i++) {
IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
try {
if (!presence.isAvailable())
mAvatarIdmap.remove(StringUtils.parseBareAddress(presence.getFrom()));
if (presence.getStatus() == null || "".equals(presence.getStatus())) {
presence.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(presence)));
}
listener.onPresenceChanged(new PresenceAdapter(presence));
} catch (RemoteException e) {
Log.w(TAG, "Error while updating roster presence entries", e);
}
}
mRemoteRosListeners.finishBroadcast();
}
}
/**
* Listener on avatar metadata event.
*
*/
private class AvatarEventListener implements AvatarListener {
/**
* Constructor.
*/
public AvatarEventListener() { }
@Override
public void onAvatarChange(String from, String avatarId, List<Info> avatarInfos) {
String bare = StringUtils.parseBareAddress(from);
if (avatarId == null)
mAvatarIdmap.remove(bare);
else {
mAvatarIdmap.put(bare, avatarId);
}
}
}
}