ImpsContactListManager.java
上传用户:szyujian
上传日期:2016-09-20
资源大小:320k
文件大小:35k
源码类别:

android开发

开发平台:

C/C++

  1. /*
  2.  * Copyright (C) 2007-2008 Esmertec AG.
  3.  * Copyright (C) 2007-2008 The Android Open Source Project
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  *  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package com.android.im.imps;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Vector;
  21. import com.android.im.engine.Address;
  22. import com.android.im.engine.Contact;
  23. import com.android.im.engine.ContactList;
  24. import com.android.im.engine.ContactListListener;
  25. import com.android.im.engine.ContactListManager;
  26. import com.android.im.engine.ImConnection;
  27. import com.android.im.engine.ImErrorInfo;
  28. import com.android.im.engine.ImException;
  29. import com.android.im.engine.Presence;
  30. import com.android.im.engine.SubscriptionRequestListener;
  31. import com.android.im.imps.ImpsConstants.ImpsVersion;
  32. /**
  33.  * An implementation of ContactListManager of Wireless Village IMPS protocol.
  34.  */
  35. public class ImpsContactListManager extends ContactListManager
  36.         implements ServerTransactionListener {
  37.     private ImpsConnection mConnection;
  38.     private String mDefaultDomain;
  39.     ImpsTransactionManager mTransactionManager;
  40.     boolean mAllowAutoSubscribe = true;
  41.     ArrayList<Contact> mSubscriptionRequests;
  42.     /**
  43.      * Constructs the manager with specific connection.
  44.      *
  45.      * @param connection the connection related to the manager
  46.      */
  47.     ImpsContactListManager(ImpsConnection connection) {
  48.         mConnection = connection;
  49.         mDefaultDomain = connection.getConfig().getDefaultDomain();
  50.         mTransactionManager = connection.getTransactionManager();
  51.         mTransactionManager.setTransactionListener(ImpsTags.PresenceNotification_Request, this);
  52.         mTransactionManager.setTransactionListener(ImpsTags.PresenceAuth_Request, this);
  53.     }
  54.     @Override
  55.     public Contact createTemporaryContact(String address){
  56.         ImpsAddress impsAddr = new ImpsUserAddress(normalizeAddress(address));
  57.         return new Contact(impsAddr, impsAddr.getScreenName());
  58.     }
  59.     @Override
  60.     public String normalizeAddress(String address) {
  61.         String s = address.toLowerCase();
  62.         if (!s.startsWith(ImpsConstants.ADDRESS_PREFIX)) {
  63.             s = ImpsConstants.ADDRESS_PREFIX + s;
  64.         }
  65.         if (mDefaultDomain != null && s.indexOf('@') == -1) {
  66.             s = s + "@" + mDefaultDomain;
  67.         }
  68.         return s;
  69.     }
  70.     @Override
  71.     public synchronized void loadContactListsAsync() {
  72.         if (getState() != LISTS_NOT_LOADED) {
  73.             return;
  74.         }
  75.         setState(LISTS_LOADING);
  76.         // load blocked list first
  77.         Primitive request = new Primitive(ImpsTags.GetBlockedList_Request);
  78.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  79.             @Override
  80.             public void onResponseError(ImpsErrorInfo error) {
  81.                 // don't notify the 501 not implemented error
  82.                 if (error.getCode() != ImpsConstants.STATUS_NOT_IMPLEMENTED) {
  83.                     notifyContactError(
  84.                             ContactListListener.ERROR_LOADING_BLOCK_LIST,
  85.                             error, null, null);
  86.                 }
  87.                 next();
  88.             }
  89.             @Override
  90.             public void onResponseOk(Primitive response) {
  91.                 extractBlockedContacts(response);
  92.                 next();
  93.             }
  94.             private void next() {
  95.                 setState(BLOCKED_LIST_LOADED);
  96.                 new LoadContactsTransaction().startGetContactLists();
  97.                 //createDefaultAttributeListAsync();
  98.             }
  99.         };
  100.         tx.sendRequest(request);
  101.     }
  102.     Vector<ImpsContactListAddress> extractListAddresses(Primitive response){
  103.         Vector<ImpsContactListAddress> addresses = new Vector<ImpsContactListAddress>();
  104.         for (PrimitiveElement child : response.getContentElement()
  105.                 .getChildren()) {
  106.             if (child.getTagName().equals(ImpsTags.ContactList)) {
  107.                 // FIXME: ignore the PEP contact lists for now
  108.                 // PEP: "Presence Enhanced Phonebook and Instant Messaging
  109.                 // Application Category" specification from Nokia and SonyEricsson.
  110.                 //  ~IM_subscriptions
  111.                 //  ~pep1.0_privatelist
  112.                 //  ~pep1.0_blocklist
  113.                 //  ~pep1.0_friendlist
  114.                 //  ~pep1.0_subscriptions-*
  115.                 if (child.getContents().contains("/~pep1.0_")) {
  116.                     continue;
  117.                 }
  118.                 addresses.add(new ImpsContactListAddress(child.getContents()));
  119.             }
  120.         }
  121.         String defaultListAddress = response.getElementContents(ImpsTags.DefaultContactList);
  122.         if (null != defaultListAddress) {
  123.             addresses.add(new ImpsContactListAddress(defaultListAddress));
  124.         }
  125.         return addresses;
  126.     }
  127. //    void createDefaultAttributeListAsync() {
  128. //        Primitive request = new Primitive(ImpsTags.CreateAttributeList_Request);
  129. //
  130. //        PrimitiveElement presenceList = request.addElement(ImpsTags.PresenceSubList);
  131. //        presenceList.setAttribute(ImpsTags.XMLNS, mConnection.getConfig().getPresenceNs());
  132. //
  133. //        for (String tagName : ImpsClientCapability.getSupportedPresenceAttribs()) {
  134. //            presenceList.addChild(tagName);
  135. //        }
  136. //
  137. //        request.addElement(ImpsTags.DefaultList, true);
  138. //
  139. //        AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  140. //            @Override
  141. //            public void onResponseError(ImpsErrorInfo error) {
  142. //                // don't notify the 501 not implemented error
  143. //                if(error.getCode() != ImpsConstants.STATUS_NOT_IMPLEMENTED) {
  144. //                    // TODO: not ERROR_RETRIEVING_PRESENCE exactly here...
  145. //                    notifyContactError(
  146. //                            ContactListListener.ERROR_RETRIEVING_PRESENCE,
  147. //                            error, null, null);
  148. //                }
  149. //            }
  150. //
  151. //            @Override
  152. //            public void onResponseOk(Primitive response) {}
  153. //        };
  154. //
  155. //        tx.sendRequest(request);
  156. //    }
  157.     @Override
  158.     public void approveSubscriptionRequest(String contact) {
  159.         handleSubscriptionRequest(contact, true);
  160.     }
  161.     @Override
  162.     public void declineSubscriptionRequest(String contact) {
  163.         handleSubscriptionRequest(contact, false);
  164.     }
  165.     private void handleSubscriptionRequest(final String contact, final boolean accept) {
  166.         Primitive request = new Primitive(ImpsTags.PresenceAuthUser);
  167.         request.addElement(ImpsTags.UserID, contact);
  168.         request.addElement(ImpsTags.Acceptance, accept);
  169.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager){
  170.             @Override
  171.             public void onResponseError(ImpsErrorInfo error) {
  172.                 SubscriptionRequestListener listener = getSubscriptionRequestListener();
  173.                 if (listener != null) {
  174.                     if (accept) {
  175.                         listener.onApproveSubScriptionError(contact, error);
  176.                     } else {
  177.                         listener.onDeclineSubScriptionError(contact, error);
  178.                     }
  179.                 }
  180.             }
  181.             @Override
  182.             public void onResponseOk(Primitive response) {
  183.                 SubscriptionRequestListener listener = getSubscriptionRequestListener();
  184.                 if (listener != null) {
  185.                     if (accept) {
  186.                         listener.onSubscriptionApproved(contact);
  187.                     } else {
  188.                         listener.onSubscriptionDeclined(contact);
  189.                     }
  190.                 }
  191.             }
  192.         };
  193.         tx.sendRequest(request);
  194.     }
  195.     void subscribeToAllListAsync() {
  196.         AsyncCompletion completion = new AsyncCompletion(){
  197.             public void onComplete() {
  198.                 // do nothing
  199.             }
  200.             public void onError(ImErrorInfo error) {
  201.                 notifyContactError(ContactListListener.ERROR_RETRIEVING_PRESENCE,
  202.                         error, null, null);
  203.             }
  204.         };
  205.         subscribeToListsAsync(mContactLists,completion);
  206.     }
  207.     void subscribeToListAsync(final ContactList list, final AsyncCompletion completion) {
  208.         Vector<ContactList> lists = new Vector<ContactList>();
  209.         lists.add(list);
  210.         subscribeToListsAsync(lists, completion);
  211.     }
  212.     void subscribeToListsAsync(final Vector<ContactList> contactLists,
  213.             final AsyncCompletion completion) {
  214.         if (contactLists.isEmpty()) {
  215.             return;
  216.         }
  217.         Primitive request = buildSubscribeToListsRequest(contactLists);
  218.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  219.             @Override
  220.             public void onResponseError(ImpsErrorInfo error) {
  221.                 if (error.getCode() == ImpsConstants.STATUS_AUTO_SUBSCRIPTION_NOT_SUPPORTED) {
  222.                     mAllowAutoSubscribe = false;
  223.                     ArrayList<Contact> contacts = new ArrayList<Contact>();
  224.                     for (ContactList list : contactLists) {
  225.                         contacts.addAll(list.getContacts());
  226.                     }
  227.                     subscribeToContactsAsync(contacts, completion);
  228.                 } else {
  229.                     completion.onError(error);
  230.                 }
  231.             }
  232.             @Override
  233.             public void onResponseOk(Primitive response) {
  234.                 completion.onComplete();
  235.             }
  236.         };
  237.         tx.sendRequest(request);
  238.     }
  239.     void subscribeToContactsAsync(ArrayList<Contact> contacts, AsyncCompletion completion) {
  240.         Primitive request = buildSubscribeToContactsRequest(contacts);
  241.         SimpleAsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager, completion);
  242.         tx.sendRequest(request);
  243.     }
  244.     void unsubscribeToListAsync(ContactList list, AsyncCompletion completion) {
  245.         Primitive request = new Primitive(ImpsTags.UnsubscribePresence_Request);
  246.         request.addElement(ImpsTags.ContactList, list.getAddress().getFullName());
  247.         SimpleAsyncTransaction tx = new SimpleAsyncTransaction(
  248.                 mTransactionManager, completion);
  249.         tx.sendRequest(request);
  250.     }
  251.     void unsubscribeToContactAsync(Contact contact, AsyncCompletion completion) {
  252.         Primitive request = new Primitive(ImpsTags.UnsubscribePresence_Request);
  253.         request.addElement(ImpsTags.User).addPropertyChild(ImpsTags.UserID,
  254.                 contact.getAddress().getFullName());
  255.         SimpleAsyncTransaction tx = new SimpleAsyncTransaction(
  256.                 mTransactionManager, completion);
  257.         tx.sendRequest(request);
  258.     }
  259.     private Primitive buildSubscribeToContactsRequest(ArrayList<Contact> contacts) {
  260.         ArrayList<ImpsAddress> addresses = new ArrayList<ImpsAddress>();
  261.         for (Contact contact : contacts) {
  262.             addresses.add((ImpsAddress)contact.getAddress());
  263.         }
  264.         Primitive request = buildSubscribePresenceRequest(addresses);
  265.         return request;
  266.     }
  267.     @Override
  268.     protected void doCreateContactListAsync(final String name,
  269.             Collection<Contact> contacts,
  270.             final boolean isDefault) {
  271.         ImpsAddress selfAddress = mConnection.getSession().getLoginUserAddress();
  272.         ImpsAddress listAddress = new ImpsContactListAddress(selfAddress, name);
  273.         final ContactList list = new ContactList(listAddress, name,
  274.                 isDefault, contacts, this);
  275.         Primitive createListRequest = buildCreateListReq(name, contacts,
  276.                 isDefault, listAddress);
  277.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  278.             @Override
  279.             public void onResponseError(ImpsErrorInfo error) {
  280.                 notifyContactError(ContactListListener.ERROR_CREATING_LIST,
  281.                         error, name, null);
  282.             }
  283.             @Override
  284.             public void onResponseOk(Primitive response) {
  285.                 subscribeToListAsync(list, new AsyncCompletion () {
  286.                     public void onComplete() {
  287.                         notifyContactListCreated(list);
  288.                     }
  289.                     public void onError(ImErrorInfo error) {
  290.                         ImpsLog.log("Error subscribing to newly created list: "
  291.                                 + error.getDescription() + "; ignored");
  292.                         onComplete();
  293.                     }
  294.                 });
  295.             }
  296.         };
  297.         tx.sendRequest(createListRequest);
  298.     }
  299.     private Primitive buildCreateListReq(String name,
  300.             Collection<Contact> contacts, boolean isDefault,
  301.             ImpsAddress listAddress) {
  302.         Primitive createListRequest = new Primitive(ImpsTags.CreateList_Request);
  303.         createListRequest.addElement(listAddress.toPrimitiveElement());
  304.         // add initial contacts, if any
  305.         if (null != contacts && !contacts.isEmpty()) {
  306.             PrimitiveElement nickList = createListRequest.addElement(ImpsTags.NickList);
  307.             for (Contact contact : contacts) {
  308.                 nickList.addChild(buildNickNameElem(contact));
  309.             }
  310.         }
  311.         PrimitiveElement contactListProp = createListRequest.addElement(
  312.                 ImpsTags.ContactListProperties);
  313.         contactListProp.addPropertyChild(ImpsConstants.DisplayName, name);
  314.         contactListProp.addPropertyChild(ImpsConstants.Default,
  315.                 ImpsUtils.toImpsBool(isDefault));
  316.         return createListRequest;
  317.     }
  318.     /**
  319.      * Delete a specified contact list asyncLoginWrapper.
  320.      *
  321.      * @param list the contact list to be deleted
  322.      */
  323.     @Override
  324.     public void doDeleteContactListAsync(final ContactList list) {
  325.         Primitive delListRequest = buildDelListReq(list);
  326.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  327.             @Override
  328.             public void onResponseError(ImpsErrorInfo error) {
  329.                 notifyContactError(ContactListListener.ERROR_DELETING_LIST,
  330.                         error, list.getName(), null);
  331.             }
  332.             @Override
  333.             public void onResponseOk(Primitive response) {
  334.                 notifyContactListDeleted(list);
  335.                 if (!mAllowAutoSubscribe) {
  336.                     unsubscribeToListAsync(list, new AsyncCompletion(){
  337.                         public void onComplete() {}
  338.                         public void onError(ImErrorInfo error) {
  339.                             // don't bother to alert this error since the
  340.                             // list has already been removed.
  341.                             ImpsLog.log("Warning: unsubscribing list presence failed");
  342.                         }
  343.                     });
  344.                 }
  345.             }
  346.         };
  347.         tx.sendRequest(delListRequest);
  348.     }
  349.     private Primitive buildDelListReq(final ContactList list) {
  350.         Primitive delListRequest = new Primitive(ImpsTags.DeleteList_Request);
  351.         delListRequest.addElement(((ImpsAddress)list.getAddress())
  352.                 .toPrimitiveElement());
  353.         return delListRequest;
  354.     }
  355.     private Primitive buildListManageRequest(ContactList list, Collection<Contact> contactsToAdd,
  356.             Collection<Contact> contactsToRemove, String listName) {
  357.         // Create ListManage request
  358.         Primitive req = new Primitive(ImpsTags.ListManage_Request);
  359.         req.addElement(((ImpsAddress)list.getAddress()).toPrimitiveElement());
  360.         req.addElement(ImpsTags.ReceiveList, false);
  361.         // If there are any pending added contacts, add them to the addNickList
  362.         if (contactsToAdd != null && !contactsToAdd.isEmpty()) {
  363.             PrimitiveElement addList = req.addElement(ImpsTags.AddNickList);
  364.             for (Contact c : contactsToAdd) {
  365.                 PrimitiveElement nickNameElem = addList.addChild(ImpsTags.NickName);
  366.                 nickNameElem.addChild(ImpsTags.Name, c.getName());
  367.                 nickNameElem.addChild(ImpsTags.UserID, c.getAddress().getFullName());
  368.             }
  369.         }
  370.         // If there are any pending removed contacts, add them to the removeNickList
  371.         if (contactsToRemove != null && !contactsToRemove.isEmpty()) {
  372.             PrimitiveElement removeList = req.addElement(ImpsTags.RemoveNickList);
  373.             for (Contact c : contactsToRemove) {
  374.                 removeList.addChild(ImpsTags.UserID, c.getAddress().getFullName());
  375.             }
  376.         }
  377.         // Add the list properties
  378.         if (listName != null) {
  379.             PrimitiveElement requestProps = req.addElement(ImpsTags.ContactListProperties);
  380.             requestProps.addPropertyChild(ImpsConstants.DisplayName, listName);
  381.         }
  382.         return req;
  383.     }
  384.     private Primitive buildSubscribeToListsRequest(Collection<ContactList> lists) {
  385.         ArrayList<ImpsAddress> addresses = new ArrayList<ImpsAddress>();
  386.         for (ContactList list : lists) {
  387.             addresses.add((ImpsAddress)list.getAddress());
  388.         }
  389.         Primitive subscribePresenceRequest = buildSubscribePresenceRequest(addresses);
  390.         subscribePresenceRequest.addElement(ImpsTags.AutoSubscribe, true);
  391.         return subscribePresenceRequest;
  392.     }
  393.     private Primitive buildSubscribePresenceRequest(ArrayList<ImpsAddress> addresses) {
  394.         Primitive request = new Primitive(ImpsTags.SubscribePresence_Request);
  395.         // XXX: Workaround on OZ IMPS GTalk server which only supports a few
  396.         // basic presence attributes. The PresenceSubList is optional and an
  397.         // empty List or missing list indicates all available presence
  398.         // attributes are desired but the OZ server doens't quite follow the
  399.         // spec here. It won't send any PresenceNotification either when we
  400.         // don't send PresenceSubList or we request more PA than it supports.
  401.         if(mConnection.getConfig().supportBasicPresenceOnly()){
  402.             PrimitiveElement presenceList = request.addElement(ImpsTags.PresenceSubList);
  403.             presenceList.setAttribute(ImpsTags.XMLNS, mConnection.getConfig().getPresenceNs());
  404.             for(String pa : ImpsClientCapability.getBasicPresenceAttributes()) {
  405.                 presenceList.addChild(pa);
  406.             }
  407.         }
  408.         for (ImpsAddress address : addresses) {
  409.             request.addElement(address.toPrimitiveElement());
  410.         }
  411.         return request;
  412.     }
  413.     public void notifyServerTransaction(ServerTransaction tx) {
  414.         Primitive request = tx.getRequest();
  415.         String type = request.getType();
  416.         if (ImpsTags.PresenceNotification_Request.equals(type)) {
  417.             tx.sendStatusResponse(ImpsConstants.SUCCESS_CODE);
  418.             ArrayList<Contact> updated = new ArrayList<Contact>();
  419.             PresenceMapping presenceMapping = mConnection.getConfig().getPresenceMapping();
  420.             for (PrimitiveElement presenceElem : request.getContentElement().getChildren()) {
  421.                 String userId = presenceElem.getChildContents(ImpsTags.UserID);
  422.                 if (userId == null) {
  423.                     continue;
  424.                 }
  425.                 PrimitiveElement presenceSubList = presenceElem.getChild(ImpsTags.PresenceSubList);
  426.                 Presence presence = ImpsPresenceUtils.extractPresence(presenceSubList, presenceMapping);
  427.                 // Find out the contact in all lists and update their presence
  428.                 for(ContactList list : mContactLists) {
  429.                     Contact contact = list.getContact(userId);
  430.                     if (contact != null) {
  431.                         contact.setPresence(presence);
  432.                         updated.add(contact);
  433.                     }
  434.                 }
  435.             }
  436.             if (updated.size() > 0) {
  437.                 notifyContactsPresenceUpdated(updated.toArray(new Contact[updated.size()]));
  438.             }
  439.         } else if (ImpsTags.PresenceAuth_Request.equals(type)) {
  440.             tx.sendStatusResponse(ImpsConstants.SUCCESS_CODE);
  441.             String userId = request.getElementContents(ImpsTags.UserID);
  442.             Contact contact = getContact(userId);
  443.             if (contact == null) {
  444.                 ImpsAddress address = new ImpsUserAddress(userId);
  445.                 contact = new Contact(address, address.getScreenName());
  446.             }
  447.             if (getState() < LISTS_LOADED) {
  448.                 if (mSubscriptionRequests == null) {
  449.                     mSubscriptionRequests = new ArrayList<Contact>();
  450.                 }
  451.                 mSubscriptionRequests.add(contact);
  452.             } else {
  453.                 SubscriptionRequestListener listener = getSubscriptionRequestListener();
  454.                 if (listener != null) {
  455.                     listener.onSubScriptionRequest(contact);
  456.                 }
  457.             }
  458.         }
  459.     }
  460.     void loadContactsOfListAsync(final ImpsAddress address, final
  461.             AsyncCompletion completion) {
  462.         Primitive listManageRequest = new Primitive(ImpsTags.ListManage_Request);
  463.         listManageRequest.addElement(address.toPrimitiveElement());
  464.         listManageRequest.addElement(ImpsTags.ReceiveList, true);
  465.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  466.             @Override
  467.             public void onResponseError(ImpsErrorInfo error) {
  468.                 completion.onError(error);
  469.             }
  470.             @Override
  471.             public void onResponseOk(Primitive response) {
  472.                 final ContactList list = extractContactList(response, address);
  473.                 mContactLists.add(list);
  474.                 if (list.isDefault()) {
  475.                     mDefaultContactList = list;
  476.                 }
  477.                 subscribeToListAsync(list, completion);
  478.             }
  479.         };
  480.         tx.sendRequest(listManageRequest);
  481.     }
  482.     private Primitive buildBlockContactReq(String address, boolean block) {
  483.         Primitive request = new Primitive(ImpsTags.BlockEntity_Request);
  484.         ImpsVersion version = mConnection.getConfig().getImpsVersion();
  485.         if (version == ImpsVersion.IMPS_VERSION_13) {
  486.             request.addElement(ImpsTags.BlockListInUse, true);
  487.             request.addElement(ImpsTags.GrantListInUse, false);
  488.         }
  489.         PrimitiveElement blockList = request.addElement(ImpsTags.BlockList);
  490.         if (version != ImpsVersion.IMPS_VERSION_13) {
  491.             blockList.addChild(ImpsTags.InUse, true);
  492.         }
  493.         PrimitiveElement entityList = blockList.addChild(block ?
  494.                 ImpsTags.AddList : ImpsTags.RemoveList);
  495.         entityList.addChild(ImpsTags.UserID, address);
  496.         return request;
  497.     }
  498.     @Override
  499.     protected void doBlockContactAsync(String address, final boolean block) {
  500.         Primitive request = buildBlockContactReq(address, block);
  501.         final Address contactAddress = new ImpsUserAddress(address);
  502.         AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {
  503.             @Override
  504.             public void onResponseError(ImpsErrorInfo error) {
  505.                 Contact c = getContact(contactAddress);
  506.                 if(c == null) {
  507.                     c = new Contact(contactAddress, contactAddress.getScreenName());
  508.                 }
  509.                 notifyContactError(
  510.                         block ? ContactListListener.ERROR_BLOCKING_CONTACT
  511.                                 : ContactListListener.ERROR_UNBLOCKING_CONTACT,
  512.                         error, null, c);
  513.             }
  514.             @Override
  515.             public void onResponseOk(Primitive response) {
  516.                 Contact c = getContact(contactAddress);
  517.                 if(c == null) {
  518.                     c = new Contact(contactAddress, contactAddress.getScreenName());
  519.                 }
  520.                 notifyBlockContact(c, block);
  521.             }
  522.         };
  523.         tx.sendRequest(request);
  524.     }
  525.     void extractBlockedContacts(Primitive response) {
  526.         mBlockedList.clear();
  527.         PrimitiveElement blockList = response.getElement(ImpsTags.BlockList);
  528.         if(blockList == null) {
  529.             return;
  530.         }
  531.         PrimitiveElement entityList = blockList.getChild(ImpsTags.EntityList);
  532.         if(entityList == null) {
  533.             return;
  534.         }
  535.         for (PrimitiveElement entity : entityList.getChildren()) {
  536.             if (entity.getTagName().equals(ImpsTags.UserID)) {
  537.                 ImpsAddress userAddress = new ImpsUserAddress(entity.getContents());
  538.                 notifyBlockContact(new Contact(userAddress, userAddress.getScreenName()),
  539.                         true);
  540.             }
  541.         }
  542.     }
  543.     /**
  544.      * Generate NickName element for a specific contact.
  545.      *
  546.      * @param contact the contact which provides the info of the NickName elem
  547.      * @return
  548.      */
  549.     private PrimitiveElement buildNickNameElem(Contact contact) {
  550.         PrimitiveElement nickName = new PrimitiveElement(ImpsTags.NickName);
  551.         nickName.addChild(ImpsTags.Name, contact.getName());
  552.         nickName.addChild(((ImpsAddress)contact.getAddress()).toPrimitiveElement());
  553.         return nickName;
  554.     }
  555.     ContactList extractContactList(Primitive response, final ImpsAddress address) {
  556.         String screenName = address.getScreenName();
  557.         boolean isDefault = false;
  558.         PrimitiveElement propertyElem = response.getElement(ImpsTags.ContactListProperties);
  559.         if (null != propertyElem) {
  560.             for (PrimitiveElement elem : propertyElem.getChildren()) {
  561.                 if (elem.getTagName().equals(ImpsTags.Property)) {
  562.                     String name = elem.getChildContents(ImpsTags.Name);
  563.                     String value = elem.getChildContents(ImpsTags.Value);
  564.                     if (name.equals(ImpsConstants.DisplayName)) {
  565.                         screenName = value;
  566.                     } else if (name.equals(ImpsTags.Default)) {
  567.                         isDefault = ImpsUtils.isTrue(value);
  568.                     }
  569.                 }
  570.             }
  571.         }
  572.         PrimitiveElement nickListElem = response.getElement(ImpsTags.NickList);
  573.         if (null == nickListElem) {
  574.             return new ContactList(address, screenName, isDefault, null, this);
  575.         }
  576.         Vector<Contact> contacts = new Vector<Contact>();
  577.         for (PrimitiveElement elem : nickListElem.getChildren()) {
  578.             String id = null;
  579.             String name = null;
  580.             String tag = elem.getTagName();
  581.             if (tag.equals(ImpsTags.NickName)) {
  582.                 id = elem.getChild(ImpsTags.UserID).getContents();
  583.                 name = elem.getChild(ImpsTags.Name).getContents();
  584.             } else if (tag.equals(ImpsTags.UserID)){
  585.                 id = elem.getContents();
  586.             }
  587.             if (id != null) {
  588.                 Address contactAddress = new ImpsUserAddress(id);
  589.                 Contact c = getContact(contactAddress);
  590.                 if (c == null) {
  591.                     if (name == null) {
  592.                         name = contactAddress.getScreenName();
  593.                     }
  594.                     c = new Contact(contactAddress, name);
  595.                 }
  596.                 contacts.add(c);
  597.             }
  598.         }
  599.         return new ContactList(address, screenName, isDefault, contacts, this);
  600.     }
  601.     private class LoadContactsTransaction extends AsyncTransaction {
  602.         Vector<ImpsContactListAddress> mListAddresses;
  603.         LoadContactsTransaction() {
  604.             super(mTransactionManager);
  605.             mListAddresses = new Vector<ImpsContactListAddress>();
  606.         }
  607.         @Override
  608.         public void onResponseError(ImpsErrorInfo error) {
  609.             notifyContactError(ContactListListener.ERROR_LOADING_LIST,
  610.                     error, null, null);
  611.         }
  612.         @Override
  613.         public void onResponseOk(Primitive response) {
  614.             mContactLists.clear();
  615.             mListAddresses = extractListAddresses(response);
  616.             if (!mListAddresses.isEmpty()) {
  617.                 fetchContacts();
  618.             } else {
  619.                 onContactListsLoaded();
  620.             }
  621.         }
  622.         void startGetContactLists() {
  623.             Primitive getListRequest = new Primitive(ImpsTags.GetList_Request);
  624.             sendRequest(getListRequest);
  625.         }
  626.         private void fetchContacts() {
  627.             ImpsContactListAddress fisrtListAddress = mListAddresses.firstElement();
  628.             loadContactsOfListAsync(fisrtListAddress, new LoadListCompletion());
  629.         }
  630.         private final class LoadListCompletion implements AsyncCompletion {
  631.             private int listIndex;
  632.             LoadListCompletion() {
  633.                 listIndex = 0;
  634.             }
  635.             public void onComplete() {
  636.                 processResult(null);
  637.             }
  638.             public void onError(ImErrorInfo error) {
  639.                 processResult(error);
  640.             }
  641.             private void processResult(ImErrorInfo error) {
  642.                 ImpsAddress addr = mListAddresses.get(listIndex);
  643.                 if (error == null) {
  644.                     notifyContactListLoaded(getContactList(addr));
  645.                 } else {
  646.                     notifyContactError(ContactListListener.ERROR_LOADING_LIST,
  647.                             error, addr.getScreenName(), null);
  648.                 }
  649.                 listIndex++;
  650.                 if (listIndex < mListAddresses.size()) {
  651.                     loadContactsOfListAsync(mListAddresses.get(listIndex), this);
  652.                 } else {
  653.                     onContactListsLoaded();
  654.                 }
  655.             }
  656.         }
  657.     }
  658.     void onContactListsLoaded() {
  659.         notifyContactListsLoaded();
  660.         // notify the pending subscription requests received before contact lists has been loaded.
  661.         SubscriptionRequestListener listener = getSubscriptionRequestListener();
  662.         if (mSubscriptionRequests != null && listener != null) {
  663.             for (Contact c : mSubscriptionRequests) {
  664.                 listener.onSubScriptionRequest(c);
  665.             }
  666.         }
  667.         ((ImpsChatSessionManager) mConnection.getChatSessionManager()).start();
  668.     }
  669.     @Override
  670.     protected void doAddContactToListAsync(String addressStr, ContactList list)
  671.         throws ImException {
  672.         ImpsUserAddress address = new ImpsUserAddress(addressStr);
  673.         Contact contact;
  674.         if (getContact(address) != null) {
  675.             contact = getContact(address);
  676.         } else {
  677.             contact = new Contact(address, address.getScreenName());
  678.         }
  679.         if (isBlocked(contact)) {
  680.             throw new ImException(ImErrorInfo.CANT_ADD_BLOCKED_CONTACT,
  681.             "Contact has been blocked");
  682.         }
  683.         addContactToListAsync(contact, list);
  684.     }
  685.     private void addContactToListAsync(final Contact contact,
  686.             final ContactList list) {
  687.         final ArrayList<Contact> contacts = new ArrayList<Contact>();
  688.         contacts.add(contact);
  689.         updateContactListAsync(list, contacts, null, null, new AsyncCompletion(){
  690.             public void onComplete() {
  691.                 notifyContactListUpdated(list,
  692.                         ContactListListener.LIST_CONTACT_ADDED, contact);
  693.                 AsyncCompletion subscribeCompletion =  new AsyncCompletion(){
  694.                     public void onComplete() {}
  695.                     public void onError(ImErrorInfo error) {
  696.                         notifyContactError(
  697.                                 ContactListListener.ERROR_RETRIEVING_PRESENCE,
  698.                                 error, list.getName(), contact);
  699.                     }
  700.                 };
  701.                 if (mAllowAutoSubscribe) {
  702.                     // XXX Send subscription again after add contact to make sure we
  703.                     // can get the presence notification. Although the we set
  704.                     // AutoSubscribe True when subscribe presence after load contacts,
  705.                     // the server might not send presence notification.
  706.                     subscribeToListAsync(list, subscribeCompletion);
  707.                 } else {
  708.                     subscribeToContactsAsync(contacts, subscribeCompletion);
  709.                 }
  710.             }
  711.             public void onError(ImErrorInfo error) {
  712.                 notifyContactError(ContactListListener.ERROR_ADDING_CONTACT,
  713.                         error, list.getName(), contact);
  714.             }
  715.         });
  716.     }
  717.     @Override
  718.     protected void doRemoveContactFromListAsync(final Contact contact, final ContactList list) {
  719.         ArrayList<Contact> contacts = new ArrayList<Contact>();
  720.         contacts.add(contact);
  721.         updateContactListAsync(list, null, contacts, null, new AsyncCompletion(){
  722.             public void onComplete() {
  723.                 ImpsLog.log("removed contact");
  724.                 notifyContactListUpdated(list,
  725.                         ContactListListener.LIST_CONTACT_REMOVED, contact);
  726.                 if (!mAllowAutoSubscribe) {
  727.                     unsubscribeToContactAsync(contact, new AsyncCompletion(){
  728.                         public void onComplete() {}
  729.                         public void onError(ImErrorInfo error) {
  730.                             // don't bother to alert this error since the
  731.                             // contact has already been removed.
  732.                             ImpsLog.log("Warning: unsubscribing contact presence failed");
  733.                         }
  734.                     });
  735.                 }
  736.             }
  737.             public void onError(ImErrorInfo error) {
  738.                 ImpsLog.log("remove contact error:" + error);
  739.                 notifyContactError(ContactListListener.ERROR_REMOVING_CONTACT,
  740.                         error, list.getName(), contact);
  741.             }
  742.         });
  743.     }
  744.     @Override
  745.     protected void setListNameAsync(final String name, final ContactList list) {
  746.         updateContactListAsync(list, null, null, name, new AsyncCompletion(){
  747.             public void onComplete() {
  748.                 notifyContactListNameUpdated(list, name);
  749.             }
  750.             public void onError(ImErrorInfo error) {
  751.                 notifyContactError(ContactListListener.ERROR_RENAMING_LIST,
  752.                         error, list.getName(), null);
  753.             }
  754.         });
  755.     }
  756.     private void updateContactListAsync(final ContactList list, final ArrayList<Contact>
  757.             contactsToAdd, final ArrayList<Contact> contactsToRemove,
  758.             final String listName, AsyncCompletion completion) {
  759.         Primitive request = buildListManageRequest(list, contactsToAdd,
  760.                 contactsToRemove, listName);
  761.         SimpleAsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager, completion);
  762.         tx.sendRequest(request);
  763.     }
  764.     String getPropertyValue(String propertyName, PrimitiveElement properties) {
  765.         for (PrimitiveElement property : properties.getChildren(ImpsTags.Property)) {
  766.             if (propertyName.equals(property.getChildContents(ImpsTags.Name))) {
  767.                 return property.getChildContents(ImpsTags.Value);
  768.             }
  769.         }
  770.         return null;
  771.     }
  772.     void reset() {
  773.         setState(LISTS_NOT_LOADED);
  774.     }
  775.     @Override
  776.     protected ImConnection getConnection() {
  777.         return mConnection;
  778.     }
  779. }