blob: 31ddce246da4e19eaee70d231a84d7d93fc23543 [file] [log] [blame]
/*
* Copyright (C) 2007-2008 Esmertec AG.
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.im.imps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
import com.android.im.engine.Address;
import com.android.im.engine.Contact;
import com.android.im.engine.ContactList;
import com.android.im.engine.ContactListListener;
import com.android.im.engine.ContactListManager;
import com.android.im.engine.ImConnection;
import com.android.im.engine.ImErrorInfo;
import com.android.im.engine.ImException;
import com.android.im.engine.Presence;
import com.android.im.engine.SubscriptionRequestListener;
import com.android.im.imps.ImpsConstants.ImpsVersion;
import com.android.im.plugin.PresenceMapping;
/**
* An implementation of ContactListManager of Wireless Village IMPS protocol.
*/
public class ImpsContactListManager extends ContactListManager
implements ServerTransactionListener {
private ImpsConnection mConnection;
private String mDefaultDomain;
ImpsTransactionManager mTransactionManager;
ImpsConnectionConfig mConfig;
boolean mAllowAutoSubscribe = true;
ArrayList<Contact> mSubscriptionRequests;
/**
* Constructs the manager with specific connection.
*
* @param connection the connection related to the manager
*/
ImpsContactListManager(ImpsConnection connection) {
mConnection = connection;
mConfig = connection.getConfig();
mDefaultDomain = mConfig.getDefaultDomain();
mTransactionManager = connection.getTransactionManager();
mTransactionManager.setTransactionListener(
ImpsTags.PresenceNotification_Request, this);
mTransactionManager.setTransactionListener(
ImpsTags.PresenceAuth_Request, this);
}
@Override
public Contact createTemporaryContact(String address){
ImpsAddress impsAddr = new ImpsUserAddress(normalizeAddress(address));
return new Contact(impsAddr, impsAddr.getScreenName());
}
@Override
public String normalizeAddress(String address) {
String s = address.toLowerCase();
if (!s.startsWith(ImpsConstants.ADDRESS_PREFIX)) {
s = ImpsConstants.ADDRESS_PREFIX + s;
}
if (mDefaultDomain != null && s.indexOf('@') == -1) {
s = s + "@" + mDefaultDomain;
}
return s;
}
@Override
public synchronized void loadContactListsAsync() {
if (getState() != LISTS_NOT_LOADED) {
return;
}
setState(LISTS_LOADING);
// load blocked list first
Primitive request = new Primitive(ImpsTags.GetBlockedList_Request);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
// don't notify the 501 not implemented error
if (error.getCode() != ImpsConstants.STATUS_NOT_IMPLEMENTED) {
notifyContactError(
ContactListListener.ERROR_LOADING_BLOCK_LIST,
error, null, null);
}
next();
}
@Override
public void onResponseOk(Primitive response) {
extractBlockedContacts(response);
next();
}
private void next() {
setState(BLOCKED_LIST_LOADED);
new LoadContactsTransaction().startGetContactLists();
//createDefaultAttributeListAsync();
}
};
tx.sendRequest(request);
}
Vector<ImpsContactListAddress> extractListAddresses(Primitive response){
Vector<ImpsContactListAddress> addresses = new Vector<ImpsContactListAddress>();
for (PrimitiveElement child : response.getContentElement()
.getChildren()) {
if (child.getTagName().equals(ImpsTags.ContactList)) {
// FIXME: ignore the PEP contact lists for now
// PEP: "Presence Enhanced Phonebook and Instant Messaging
// Application Category" specification from Nokia and SonyEricsson.
// ~IM_subscriptions
// ~pep1.0_privatelist
// ~pep1.0_blocklist
// ~pep1.0_friendlist
// ~pep1.0_subscriptions-*
if (child.getContents().contains("/~pep1.0_")) {
continue;
}
addresses.add(new ImpsContactListAddress(child.getContents()));
}
}
String defaultListAddress = response.getElementContents(ImpsTags.DefaultContactList);
if (null != defaultListAddress) {
addresses.add(new ImpsContactListAddress(defaultListAddress));
}
return addresses;
}
public void fetchPresence(ImpsAddress[] addresses) {
if (addresses == null || addresses.length == 0) {
return;
}
Primitive request = new Primitive(ImpsTags.GetPresence_Request);
for (ImpsAddress addr : addresses) {
request.addElement(addr.toPrimitiveElement());
}
AsyncTransaction tx = new AsyncTransaction(mTransactionManager){
@Override
public void onResponseError(ImpsErrorInfo error) {
ImpsLog.logError("Failed to get presence:" + error.toString());
}
@Override
public void onResponseOk(Primitive response) {
extractAndNotifyPresence(response.getContentElement());
}
};
tx.sendRequest(request);
}
public ImpsAddress[] getAllListAddress() {
int count = mContactLists.size();
ImpsAddress[] res = new ImpsContactListAddress[count];
int index = 0;
for (ContactList l : mContactLists) {
res[index++] = (ImpsContactListAddress) l.getAddress();
}
return res;
}
// void createDefaultAttributeListAsync() {
// Primitive request = new Primitive(ImpsTags.CreateAttributeList_Request);
//
// PrimitiveElement presenceList = request.addElement(ImpsTags.PresenceSubList);
// presenceList.setAttribute(ImpsTags.XMLNS, mConnection.getConfig().getPresenceNs());
//
// for (String tagName : ImpsClientCapability.getSupportedPresenceAttribs()) {
// presenceList.addChild(tagName);
// }
//
// request.addElement(ImpsTags.DefaultList, true);
//
// AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
// @Override
// public void onResponseError(ImpsErrorInfo error) {
// // don't notify the 501 not implemented error
// if(error.getCode() != ImpsConstants.STATUS_NOT_IMPLEMENTED) {
// // TODO: not ERROR_RETRIEVING_PRESENCE exactly here...
// notifyContactError(
// ContactListListener.ERROR_RETRIEVING_PRESENCE,
// error, null, null);
// }
// }
//
// @Override
// public void onResponseOk(Primitive response) {}
// };
//
// tx.sendRequest(request);
// }
@Override
public void approveSubscriptionRequest(String contact) {
handleSubscriptionRequest(contact, true);
}
@Override
public void declineSubscriptionRequest(String contact) {
handleSubscriptionRequest(contact, false);
}
private void handleSubscriptionRequest(final String contact, final boolean accept) {
Primitive request = new Primitive(ImpsTags.PresenceAuthUser);
request.addElement(ImpsTags.UserID, contact);
request.addElement(ImpsTags.Acceptance, accept);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager){
@Override
public void onResponseError(ImpsErrorInfo error) {
SubscriptionRequestListener listener = getSubscriptionRequestListener();
if (listener != null) {
if (accept) {
listener.onApproveSubScriptionError(contact, error);
} else {
listener.onDeclineSubScriptionError(contact, error);
}
}
}
@Override
public void onResponseOk(Primitive response) {
SubscriptionRequestListener listener = getSubscriptionRequestListener();
if (listener != null) {
if (accept) {
listener.onSubscriptionApproved(contact);
} else {
listener.onSubscriptionDeclined(contact);
}
}
}
};
tx.sendRequest(request);
}
void subscribeToAllListAsync() {
AsyncCompletion completion = new AsyncCompletion(){
public void onComplete() {
// do nothing
}
public void onError(ImErrorInfo error) {
notifyContactError(ContactListListener.ERROR_RETRIEVING_PRESENCE,
error, null, null);
}
};
subscribeToListsAsync(mContactLists,completion);
}
void subscribeToListAsync(final ContactList list, final AsyncCompletion completion) {
Vector<ContactList> lists = new Vector<ContactList>();
lists.add(list);
subscribeToListsAsync(lists, completion);
}
void subscribeToListsAsync(final Vector<ContactList> contactLists,
final AsyncCompletion completion) {
if (contactLists.isEmpty()) {
return;
}
Primitive request = buildSubscribeToListsRequest(contactLists);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
if (error.getCode()
== ImpsConstants.STATUS_AUTO_SUBSCRIPTION_NOT_SUPPORTED) {
mAllowAutoSubscribe = false;
ArrayList<Contact> contacts = new ArrayList<Contact>();
for (ContactList list : contactLists) {
contacts.addAll(list.getContacts());
}
subscribeToContactsAsync(contacts, completion);
} else {
if (completion != null) {
completion.onError(error);
}
}
}
@Override
public void onResponseOk(Primitive response) {
if (completion != null) {
completion.onComplete();
}
}
};
tx.sendRequest(request);
}
void subscribeToContactsAsync(ArrayList<Contact> contacts, AsyncCompletion completion) {
Primitive request = buildSubscribeToContactsRequest(contacts);
SimpleAsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager, completion);
tx.sendRequest(request);
}
void unsubscribeToListAsync(ContactList list, AsyncCompletion completion) {
Primitive request = new Primitive(ImpsTags.UnsubscribePresence_Request);
request.addElement(ImpsTags.ContactList, list.getAddress().getFullName());
SimpleAsyncTransaction tx = new SimpleAsyncTransaction(
mTransactionManager, completion);
tx.sendRequest(request);
}
void unsubscribeToContactAsync(Contact contact, AsyncCompletion completion) {
Primitive request = new Primitive(ImpsTags.UnsubscribePresence_Request);
request.addElement(ImpsTags.User).addPropertyChild(ImpsTags.UserID,
contact.getAddress().getFullName());
SimpleAsyncTransaction tx = new SimpleAsyncTransaction(
mTransactionManager, completion);
tx.sendRequest(request);
}
private Primitive buildSubscribeToContactsRequest(ArrayList<Contact> contacts) {
ArrayList<ImpsAddress> addresses = new ArrayList<ImpsAddress>();
for (Contact contact : contacts) {
addresses.add((ImpsAddress)contact.getAddress());
}
Primitive request = buildSubscribePresenceRequest(addresses);
return request;
}
@Override
protected void doCreateContactListAsync(final String name,
Collection<Contact> contacts,
final boolean isDefault) {
ImpsAddress selfAddress = mConnection.getSession().getLoginUserAddress();
ImpsAddress listAddress = new ImpsContactListAddress(selfAddress, name);
final ContactList list = new ContactList(listAddress, name,
isDefault, contacts, this);
Primitive createListRequest = buildCreateListReq(name, contacts,
isDefault, listAddress);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
notifyContactError(ContactListListener.ERROR_CREATING_LIST,
error, name, null);
}
@Override
public void onResponseOk(Primitive response) {
notifyContactListCreated(list);
if (mConfig.usePrensencePolling()) {
getPresencePollingManager().resetPollingContacts();
} else {
subscribeToListAsync(list, null);
}
}
};
tx.sendRequest(createListRequest);
}
private Primitive buildCreateListReq(String name,
Collection<Contact> contacts, boolean isDefault,
ImpsAddress listAddress) {
Primitive createListRequest = new Primitive(ImpsTags.CreateList_Request);
createListRequest.addElement(listAddress.toPrimitiveElement());
// add initial contacts, if any
if (null != contacts && !contacts.isEmpty()) {
PrimitiveElement nickList = createListRequest.addElement(ImpsTags.NickList);
for (Contact contact : contacts) {
nickList.addChild(buildNickNameElem(contact));
}
}
PrimitiveElement contactListProp = createListRequest.addElement(
ImpsTags.ContactListProperties);
contactListProp.addPropertyChild(ImpsConstants.DisplayName, name);
contactListProp.addPropertyChild(ImpsConstants.Default,
ImpsUtils.toImpsBool(isDefault));
return createListRequest;
}
/**
* Delete a specified contact list asyncLoginWrapper.
*
* @param list the contact list to be deleted
*/
@Override
public void doDeleteContactListAsync(final ContactList list) {
Primitive delListRequest = buildDelListReq(list);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
notifyContactError(ContactListListener.ERROR_DELETING_LIST,
error, list.getName(), null);
}
@Override
public void onResponseOk(Primitive response) {
notifyContactListDeleted(list);
if (mConfig.usePrensencePolling()) {
getPresencePollingManager().resetPollingContacts();
} else if (!mAllowAutoSubscribe) {
unsubscribeToListAsync(list, new AsyncCompletion(){
public void onComplete() {}
public void onError(ImErrorInfo error) {
// don't bother to alert this error since the
// list has already been removed.
ImpsLog.log("Warning: unsubscribing list presence failed");
}
});
}
}
};
tx.sendRequest(delListRequest);
}
private Primitive buildDelListReq(final ContactList list) {
Primitive delListRequest = new Primitive(ImpsTags.DeleteList_Request);
delListRequest.addElement(((ImpsAddress)list.getAddress())
.toPrimitiveElement());
return delListRequest;
}
private Primitive buildListManageRequest(ContactList list, Collection<Contact> contactsToAdd,
Collection<Contact> contactsToRemove, String listName) {
// Create ListManage request
Primitive req = new Primitive(ImpsTags.ListManage_Request);
req.addElement(((ImpsAddress)list.getAddress()).toPrimitiveElement());
req.addElement(ImpsTags.ReceiveList, false);
// If there are any pending added contacts, add them to the addNickList
if (contactsToAdd != null && !contactsToAdd.isEmpty()) {
PrimitiveElement addList = req.addElement(ImpsTags.AddNickList);
for (Contact c : contactsToAdd) {
PrimitiveElement nickNameElem = addList.addChild(ImpsTags.NickName);
nickNameElem.addChild(ImpsTags.Name, c.getName());
nickNameElem.addChild(ImpsTags.UserID, c.getAddress().getFullName());
}
}
// If there are any pending removed contacts, add them to the removeNickList
if (contactsToRemove != null && !contactsToRemove.isEmpty()) {
PrimitiveElement removeList = req.addElement(ImpsTags.RemoveNickList);
for (Contact c : contactsToRemove) {
removeList.addChild(ImpsTags.UserID, c.getAddress().getFullName());
}
}
// Add the list properties
if (listName != null) {
PrimitiveElement requestProps = req.addElement(ImpsTags.ContactListProperties);
requestProps.addPropertyChild(ImpsConstants.DisplayName, listName);
}
return req;
}
private Primitive buildSubscribeToListsRequest(Collection<ContactList> lists) {
ArrayList<ImpsAddress> addresses = new ArrayList<ImpsAddress>();
for (ContactList list : lists) {
addresses.add((ImpsAddress)list.getAddress());
}
Primitive subscribePresenceRequest = buildSubscribePresenceRequest(addresses);
subscribePresenceRequest.addElement(ImpsTags.AutoSubscribe, true);
return subscribePresenceRequest;
}
private Primitive buildSubscribePresenceRequest(ArrayList<ImpsAddress> addresses) {
Primitive request = new Primitive(ImpsTags.SubscribePresence_Request);
// XXX: Workaround on OZ IMPS GTalk server which only supports a few
// basic presence attributes. The PresenceSubList is optional and an
// empty List or missing list indicates all available presence
// attributes are desired but the OZ server doens't quite follow the
// spec here. It won't send any PresenceNotification either when we
// don't send PresenceSubList or we request more PA than it supports.
if(mConfig.supportBasicPresenceOnly()){
PrimitiveElement presenceList = request.addElement(ImpsTags.PresenceSubList);
presenceList.setAttribute(ImpsTags.XMLNS, mConfig.getPresenceNs());
for(String pa : ImpsClientCapability.getBasicPresenceAttributes()) {
presenceList.addChild(pa);
}
}
for (ImpsAddress address : addresses) {
request.addElement(address.toPrimitiveElement());
}
return request;
}
public void notifyServerTransaction(ServerTransaction tx) {
Primitive request = tx.getRequest();
String type = request.getType();
if (ImpsTags.PresenceNotification_Request.equals(type)) {
tx.sendStatusResponse(ImpsConstants.SUCCESS_CODE);
PrimitiveElement content = request.getContentElement();
extractAndNotifyPresence(content);
} else if (ImpsTags.PresenceAuth_Request.equals(type)) {
tx.sendStatusResponse(ImpsConstants.SUCCESS_CODE);
String userId = request.getElementContents(ImpsTags.UserID);
Contact contact = getContact(userId);
if (contact == null) {
ImpsAddress address = new ImpsUserAddress(userId);
contact = new Contact(address, address.getScreenName());
}
if (getState() < LISTS_LOADED) {
if (mSubscriptionRequests == null) {
mSubscriptionRequests = new ArrayList<Contact>();
}
mSubscriptionRequests.add(contact);
} else {
SubscriptionRequestListener listener = getSubscriptionRequestListener();
if (listener != null) {
listener.onSubScriptionRequest(contact);
}
}
}
}
private void extractAndNotifyPresence(PrimitiveElement content) {
ArrayList<Contact> updated = new ArrayList<Contact>();
PresenceMapping presenceMapping = mConfig.getPresenceMapping();
ArrayList<PrimitiveElement> presenceList = content.getChildren(ImpsTags.Presence);
for (PrimitiveElement presenceElem : presenceList) {
String userId = presenceElem.getChildContents(ImpsTags.UserID);
if (userId == null) {
continue;
}
PrimitiveElement presenceSubList = presenceElem.getChild(ImpsTags.PresenceSubList);
Presence presence = ImpsPresenceUtils.extractPresence(presenceSubList, presenceMapping);
// Find out the contact in all lists and update their presence
for(ContactList list : mContactLists) {
Contact contact = list.getContact(userId);
if (contact != null) {
contact.setPresence(presence);
updated.add(contact);
}
}
}
if (!updated.isEmpty()) {
notifyContactsPresenceUpdated(updated.toArray(new Contact[updated.size()]));
}
}
void loadContactsOfListAsync(final ImpsAddress address, final
AsyncCompletion completion) {
Primitive listManageRequest = new Primitive(ImpsTags.ListManage_Request);
listManageRequest.addElement(address.toPrimitiveElement());
listManageRequest.addElement(ImpsTags.ReceiveList, true);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
completion.onError(error);
}
@Override
public void onResponseOk(Primitive response) {
final ContactList list = extractContactList(response, address);
mContactLists.add(list);
if (list.isDefault()) {
mDefaultContactList = list;
}
if (mConfig.usePrensencePolling()) {
completion.onComplete();
} else {
subscribeToListAsync(list, completion);
}
}
};
tx.sendRequest(listManageRequest);
}
private Primitive buildBlockContactReq(String address, boolean block) {
Primitive request = new Primitive(ImpsTags.BlockEntity_Request);
ImpsVersion version = mConfig.getImpsVersion();
if (version == ImpsVersion.IMPS_VERSION_13) {
request.addElement(ImpsTags.BlockListInUse, true);
request.addElement(ImpsTags.GrantListInUse, false);
}
PrimitiveElement blockList = request.addElement(ImpsTags.BlockList);
if (version != ImpsVersion.IMPS_VERSION_13) {
blockList.addChild(ImpsTags.InUse, true);
}
PrimitiveElement entityList = blockList.addChild(block ?
ImpsTags.AddList : ImpsTags.RemoveList);
entityList.addChild(ImpsTags.UserID, address);
return request;
}
@Override
protected void doBlockContactAsync(String address, final boolean block) {
Primitive request = buildBlockContactReq(address, block);
final Address contactAddress = new ImpsUserAddress(address);
AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
@Override
public void onResponseError(ImpsErrorInfo error) {
Contact c = getContact(contactAddress);
if(c == null) {
c = new Contact(contactAddress, contactAddress.getScreenName());
}
notifyContactError(
block ? ContactListListener.ERROR_BLOCKING_CONTACT
: ContactListListener.ERROR_UNBLOCKING_CONTACT,
error, null, c);
}
@Override
public void onResponseOk(Primitive response) {
Contact c = getContact(contactAddress);
if(c == null) {
c = new Contact(contactAddress, contactAddress.getScreenName());
}
notifyBlockContact(c, block);
}
};
tx.sendRequest(request);
}
void extractBlockedContacts(Primitive response) {
mBlockedList.clear();
PrimitiveElement blockList = response.getElement(ImpsTags.BlockList);
if(blockList == null) {
return;
}
PrimitiveElement entityList = blockList.getChild(ImpsTags.EntityList);
if(entityList == null) {
return;
}
for (PrimitiveElement entity : entityList.getChildren()) {
if (ImpsTags.UserID.equals(entity.getTagName())) {
String userId = entity.getContents();
if (userId == null || userId.length() == 0) {
ImpsLog.logError("Empty UserID in BlockList");
continue;
}
ImpsAddress userAddress = new ImpsUserAddress(entity.getContents());
notifyBlockContact(new Contact(userAddress, userAddress.getScreenName()),
true);
}
}
}
/**
* Generate NickName element for a specific contact.
*
* @param contact the contact which provides the info of the NickName elem
* @return
*/
private PrimitiveElement buildNickNameElem(Contact contact) {
PrimitiveElement nickName = new PrimitiveElement(ImpsTags.NickName);
nickName.addChild(ImpsTags.Name, contact.getName());
nickName.addChild(((ImpsAddress)contact.getAddress()).toPrimitiveElement());
return nickName;
}
ContactList extractContactList(Primitive response, final ImpsAddress address) {
String screenName = address.getScreenName();
boolean isDefault = false;
PrimitiveElement propertyElem = response.getElement(ImpsTags.ContactListProperties);
if (null != propertyElem) {
for (PrimitiveElement elem : propertyElem.getChildren()) {
if (elem.getTagName().equals(ImpsTags.Property)) {
String name = elem.getChildContents(ImpsTags.Name);
String value = elem.getChildContents(ImpsTags.Value);
if (name.equals(ImpsConstants.DisplayName)) {
screenName = value;
} else if (name.equals(ImpsTags.Default)) {
isDefault = ImpsUtils.isTrue(value);
}
}
}
}
PrimitiveElement nickListElem = response.getElement(ImpsTags.NickList);
if (null == nickListElem) {
return new ContactList(address, screenName, isDefault, null, this);
}
Vector<Contact> contacts = new Vector<Contact>();
for (PrimitiveElement elem : nickListElem.getChildren()) {
String id = null;
String name = null;
String tag = elem.getTagName();
if (tag.equals(ImpsTags.NickName)) {
id = elem.getChild(ImpsTags.UserID).getContents();
name = elem.getChild(ImpsTags.Name).getContents();
} else if (tag.equals(ImpsTags.UserID)){
id = elem.getContents();
}
if (id != null) {
Address contactAddress = new ImpsUserAddress(id);
Contact c = getContact(contactAddress);
if (c == null) {
if (name == null) {
name = contactAddress.getScreenName();
}
c = new Contact(contactAddress, name);
}
contacts.add(c);
}
}
return new ContactList(address, screenName, isDefault, contacts, this);
}
private class LoadContactsTransaction extends AsyncTransaction {
Vector<ImpsContactListAddress> mListAddresses;
LoadContactsTransaction() {
super(mTransactionManager);
mListAddresses = new Vector<ImpsContactListAddress>();
}
@Override
public void onResponseError(ImpsErrorInfo error) {
notifyContactError(ContactListListener.ERROR_LOADING_LIST,
error, null, null);
}
@Override
public void onResponseOk(Primitive response) {
mContactLists.clear();
mListAddresses = extractListAddresses(response);
if (!mListAddresses.isEmpty()) {
fetchContacts();
} else {
onContactListsLoaded();
}
}
void startGetContactLists() {
Primitive getListRequest = new Primitive(ImpsTags.GetList_Request);
sendRequest(getListRequest);
}
private void fetchContacts() {
ImpsContactListAddress fisrtListAddress = mListAddresses.firstElement();
loadContactsOfListAsync(fisrtListAddress, new LoadListCompletion());
}
private final class LoadListCompletion implements AsyncCompletion {
private int mListIndex;
LoadListCompletion() {
mListIndex = 0;
}
public void onComplete() {
processResult(null);
}
public void onError(ImErrorInfo error) {
processResult(error);
}
private void processResult(ImErrorInfo error) {
ImpsAddress addr = mListAddresses.get(mListIndex);
if (error == null) {
notifyContactListLoaded(getContactList(addr));
} else {
notifyContactError(ContactListListener.ERROR_LOADING_LIST,
error, addr.getScreenName(), null);
}
mListIndex++;
if (mListIndex < mListAddresses.size()) {
loadContactsOfListAsync(mListAddresses.get(mListIndex), this);
} else {
onContactListsLoaded();
}
}
}
}
void onContactListsLoaded() {
notifyContactListsLoaded();
if (mConfig.usePrensencePolling()) {
fetchPresence(getAllListAddress());
}
// notify the pending subscription requests received before contact
// lists has been loaded.
SubscriptionRequestListener listener = getSubscriptionRequestListener();
if (mSubscriptionRequests != null && listener != null) {
for (Contact c : mSubscriptionRequests) {
listener.onSubScriptionRequest(c);
}
}
((ImpsChatSessionManager) mConnection.getChatSessionManager()).start();
}
@Override
protected void doAddContactToListAsync(String addressStr, ContactList list)
throws ImException {
ImpsUserAddress address = new ImpsUserAddress(addressStr);
Contact contact;
if (getContact(address) != null) {
contact = getContact(address);
} else {
contact = new Contact(address, address.getScreenName());
}
if (isBlocked(contact)) {
throw new ImException(ImErrorInfo.CANT_ADD_BLOCKED_CONTACT,
"Contact has been blocked");
}
addContactToListAsync(contact, list);
}
private void addContactToListAsync(final Contact contact,
final ContactList list) {
final ArrayList<Contact> contacts = new ArrayList<Contact>();
contacts.add(contact);
updateContactListAsync(list, contacts, null, null, new AsyncCompletion(){
public void onComplete() {
notifyContactListUpdated(list,
ContactListListener.LIST_CONTACT_ADDED, contact);
if (mConfig.usePrensencePolling()) {
fetchPresence(new ImpsAddress[]{
(ImpsAddress) contact.getAddress()});
} else {
AsyncCompletion subscribeCompletion = new AsyncCompletion(){
public void onComplete() {}
public void onError(ImErrorInfo error) {
notifyContactError(
ContactListListener.ERROR_RETRIEVING_PRESENCE,
error, list.getName(), contact);
}
};
if (mAllowAutoSubscribe) {
// XXX Send subscription again after add contact to make sure we
// can get the presence notification. Although the we set
// AutoSubscribe True when subscribe presence after load contacts,
// the server might not send presence notification.
subscribeToListAsync(list, subscribeCompletion);
} else {
subscribeToContactsAsync(contacts, subscribeCompletion);
}
}
}
public void onError(ImErrorInfo error) {
// XXX Workaround to convert 402 error to 531. Some
// servers might return 402 - Bad parameter instead of
// 531 - Unknown user if the user input an invalid user ID.
if (error.getCode() == ImpsErrorInfo.BAD_PARAMETER) {
error = new ImErrorInfo(ImpsErrorInfo.UNKNOWN_USER,
error.getDescription());
}
notifyContactError(ContactListListener.ERROR_ADDING_CONTACT,
error, list.getName(), contact);
}
});
}
@Override
protected void doRemoveContactFromListAsync(final Contact contact, final ContactList list) {
ArrayList<Contact> contacts = new ArrayList<Contact>();
contacts.add(contact);
updateContactListAsync(list, null, contacts, null, new AsyncCompletion(){
public void onComplete() {
ImpsLog.log("removed contact");
notifyContactListUpdated(list,
ContactListListener.LIST_CONTACT_REMOVED, contact);
if (!mAllowAutoSubscribe) {
unsubscribeToContactAsync(contact, new AsyncCompletion(){
public void onComplete() {}
public void onError(ImErrorInfo error) {
// don't bother to alert this error since the
// contact has already been removed.
ImpsLog.log("Warning: unsubscribing contact presence failed");
}
});
}
}
public void onError(ImErrorInfo error) {
ImpsLog.log("remove contact error:" + error);
notifyContactError(ContactListListener.ERROR_REMOVING_CONTACT,
error, list.getName(), contact);
}
});
}
@Override
protected void setListNameAsync(final String name, final ContactList list) {
updateContactListAsync(list, null, null, name, new AsyncCompletion(){
public void onComplete() {
notifyContactListNameUpdated(list, name);
}
public void onError(ImErrorInfo error) {
notifyContactError(ContactListListener.ERROR_RENAMING_LIST,
error, list.getName(), null);
}
});
}
private void updateContactListAsync(final ContactList list, final ArrayList<Contact>
contactsToAdd, final ArrayList<Contact> contactsToRemove,
final String listName, AsyncCompletion completion) {
Primitive request = buildListManageRequest(list, contactsToAdd,
contactsToRemove, listName);
SimpleAsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager, completion);
tx.sendRequest(request);
}
String getPropertyValue(String propertyName, PrimitiveElement properties) {
for (PrimitiveElement property : properties.getChildren(ImpsTags.Property)) {
if (propertyName.equals(property.getChildContents(ImpsTags.Name))) {
return property.getChildContents(ImpsTags.Value);
}
}
return null;
}
void reset() {
setState(LISTS_NOT_LOADED);
}
@Override
protected ImConnection getConnection() {
return mConnection;
}
private PresencePollingManager mPollingMgr;
/*package*/PresencePollingManager getPresencePollingManager() {
if (mPollingMgr == null) {
mPollingMgr = new PresencePollingManager(this,
mConfig.getPresencePollInterval());
}
return mPollingMgr;
}
}