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

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.service;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Vector;
  25. import android.content.ContentResolver;
  26. import android.content.ContentUris;
  27. import android.content.ContentValues;
  28. import android.content.Intent;
  29. import android.database.Cursor;
  30. import android.net.Uri;
  31. import android.os.RemoteException;
  32. import android.provider.Im;
  33. import android.util.Log;
  34. import android.widget.Toast;
  35. import com.android.im.IContactList;
  36. import com.android.im.IContactListListener;
  37. import com.android.im.IContactListManager;
  38. import com.android.im.ISubscriptionListener;
  39. import com.android.im.R;
  40. import com.android.im.engine.Address;
  41. import com.android.im.engine.Contact;
  42. import com.android.im.engine.ContactList;
  43. import com.android.im.engine.ContactListListener;
  44. import com.android.im.engine.ContactListManager;
  45. import com.android.im.engine.ImErrorInfo;
  46. import com.android.im.engine.ImException;
  47. import com.android.im.engine.Presence;
  48. import com.android.im.engine.SubscriptionRequestListener;
  49. public class ContactListManagerAdapter extends IContactListManager.Stub {
  50.     static final String TAG = RemoteImService.TAG;
  51.     ImConnectionAdapter mConn;
  52.     ContentResolver     mResolver;
  53.     private ContactListManager          mAdaptee;
  54.     private ContactListListenerAdapter  mContactListListenerAdapter;
  55.     private SubscriptionRequestListenerAdapter mSubscriptionListenerAdapter;
  56.     HashMap<Address, ContactListAdapter> mContactLists;
  57.     HashMap<String, Contact> mTemporaryContacts;
  58.     HashSet<String> mValidatedContactLists;
  59.     HashSet<String> mValidatedContacts;
  60.     HashSet<String> mValidatedBlockedContacts;
  61.     private long mAccountId;
  62.     private long mProviderId;
  63.     private Uri mAvatarUrl;
  64.     private Uri mContactUrl;
  65.     static final long FAKE_TEMPORARY_LIST_ID = -1;
  66.     static final String[] CONTACT_LIST_ID_PROJECTION  = { Im.ContactList._ID };
  67.     RemoteImService mContext;
  68.     public ContactListManagerAdapter(ImConnectionAdapter conn) {
  69.         mAdaptee  = conn.getAdaptee().getContactListManager();
  70.         mConn     = conn;
  71.         mContext  = conn.getContext();
  72.         mResolver = mContext.getContentResolver();
  73.         mContactListListenerAdapter = new ContactListListenerAdapter();
  74.         mSubscriptionListenerAdapter = new SubscriptionRequestListenerAdapter();
  75.         mContactLists = new HashMap<Address, ContactListAdapter>();
  76.         mTemporaryContacts = new HashMap<String, Contact>();
  77.         mValidatedContacts = new HashSet<String>();
  78.         mValidatedContactLists = new HashSet<String>();
  79.         mValidatedBlockedContacts = new HashSet<String>();
  80.         mAdaptee.addContactListListener(mContactListListenerAdapter);
  81.         mAdaptee.setSubscriptionRequestListener(mSubscriptionListenerAdapter);
  82.         mAccountId  = mConn.getAccountId();
  83.         mProviderId = mConn.getProviderId();
  84.         Uri.Builder builder = Im.Avatars.CONTENT_URI_AVATARS_BY.buildUpon();
  85.         ContentUris.appendId(builder, mProviderId);
  86.         ContentUris.appendId(builder, mAccountId);
  87.         mAvatarUrl = builder.build();
  88.         builder = Im.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
  89.         ContentUris.appendId(builder, mProviderId);
  90.         ContentUris.appendId(builder, mAccountId);
  91.         mContactUrl = builder.build();
  92.     }
  93.     public int createContactList(String name, List<Contact> contacts) {
  94.         try {
  95.             mAdaptee.createContactListAsync(name, contacts);
  96.         } catch (ImException e) {
  97.             return e.getImError().getCode();
  98.         }
  99.         return ImErrorInfo.NO_ERROR;
  100.     }
  101.     public int deleteContactList(String name) {
  102.         try {
  103.             mAdaptee.deleteContactListAsync(name);
  104.         } catch (ImException e) {
  105.             return e.getImError().getCode();
  106.         }
  107.         return ImErrorInfo.NO_ERROR;
  108.     }
  109.     public List getContactLists() {
  110.         synchronized (mContactLists) {
  111.             return new ArrayList<ContactListAdapter>(mContactLists.values());
  112.         }
  113.     }
  114.     public int removeContact(String address) {
  115.         if(isTemporary(address)) {
  116.             // For temporary contact, just close the session and delete him in
  117.             // database.
  118.             closeChatSession(address);
  119.             String selection = Im.Contacts.USERNAME + "=?";
  120.             String[] selectionArgs = { address };
  121.             mResolver.delete(mContactUrl, selection, selectionArgs);
  122.             synchronized (mTemporaryContacts) {
  123.                 mTemporaryContacts.remove(address);
  124.             }
  125.         } else {
  126.             synchronized (mContactLists) {
  127.                 Contact c = getContactByAddress(address);
  128.                 for(ContactListAdapter list : mContactLists.values()) {
  129.                     int resCode = list.removeContact(c);
  130.                     if (ImErrorInfo.NO_ERROR != resCode) {
  131.                         return resCode;
  132.                     }
  133.                 }
  134.             }
  135.         }
  136.         return ImErrorInfo.NO_ERROR;
  137.     }
  138.     public void approveSubscription(String address) {
  139.         mAdaptee.approveSubscriptionRequest(address);
  140.     }
  141.     public void declineSubscription(String address) {
  142.         mAdaptee.declineSubscriptionRequest(address);
  143.     }
  144.     public int blockContact(String address) {
  145.         try {
  146.             mAdaptee.blockContactAsync(address);
  147.         } catch (ImException e) {
  148.             return e.getImError().getCode();
  149.         }
  150.         return ImErrorInfo.NO_ERROR;
  151.     }
  152.     public int unBlockContact(String address) {
  153.         try {
  154.             mAdaptee.unblockContactAsync(address);
  155.         } catch (ImException e) {
  156.             Log.e(TAG, e.getMessage());
  157.             return e.getImError().getCode();
  158.         }
  159.         return ImErrorInfo.NO_ERROR;
  160.     }
  161.     public boolean isBlocked(String address) {
  162.         try {
  163.             return mAdaptee.isBlocked(address);
  164.         } catch (ImException e) {
  165.             Log.e(TAG, e.getMessage());
  166.             return false;
  167.         }
  168.     }
  169.     public void registerContactListListener(IContactListListener listener) {
  170.         mContactListListenerAdapter.addRemoteListener(listener);
  171.     }
  172.     public void unregisterContactListListener(IContactListListener listener) {
  173.         mContactListListenerAdapter.removeRemoteListener(listener);
  174.     }
  175.     public void registerSubscriptionListener(ISubscriptionListener listener) {
  176.         mSubscriptionListenerAdapter.addRemoteListener(listener);
  177.     }
  178.     public void unregisterSubscriptionListener(ISubscriptionListener listener) {
  179.         mSubscriptionListenerAdapter.removeRemoteListener(listener);
  180.     }
  181.     public IContactList getContactList(String name) {
  182.         return getContactListAdapter(name);
  183.     }
  184.     public void loadContactLists() {
  185.         if(mAdaptee.getState() == ContactListManager.LISTS_NOT_LOADED){
  186.             clearValidatedContactsAndLists();
  187.             mAdaptee.loadContactListsAsync();
  188.         }
  189.     }
  190.     public int getState() {
  191.         return mAdaptee.getState();
  192.     }
  193.     public Contact getContactByAddress(String address) {
  194.         Contact c = mAdaptee.getContact(address);
  195.         if(c == null) {
  196.             synchronized (mTemporaryContacts) {
  197.                 return mTemporaryContacts.get(address);
  198.             }
  199.         } else {
  200.             return c;
  201.         }
  202.     }
  203.     public Contact createTemporaryContact(String address) {
  204.         Contact c = mAdaptee.createTemporaryContact(address);
  205.         insertTemporary(c);
  206.         return c;
  207.     }
  208.     public long queryOrInsertContact(Contact c) {
  209.         long result;
  210.         String username = c.getAddress().getFullName();
  211.         String selection = Im.Contacts.USERNAME + "=?";
  212.         String[] selectionArgs = { username };
  213.         String[] projection = {Im.Contacts._ID};
  214.         Cursor cursor = mResolver.query(mContactUrl, projection, selection,
  215.                 selectionArgs, null);
  216.         if(cursor != null && cursor.moveToFirst()) {
  217.             result = cursor.getLong(0);
  218.         } else {
  219.             result = insertTemporary(c);
  220.         }
  221.         if(cursor != null) {
  222.             cursor.close();
  223.         }
  224.         return result;
  225.     }
  226.     private long insertTemporary(Contact c) {
  227.         synchronized (mTemporaryContacts) {
  228.             mTemporaryContacts.put(c.getAddress().getFullName(), c);
  229.         }
  230.         Uri uri = insertContactContent(c, FAKE_TEMPORARY_LIST_ID);
  231.         return ContentUris.parseId(uri);
  232.     }
  233.     /**
  234.      * Tells if a contact is a temporary one which is not in the list of
  235.      * contacts that we subscribe presence for. Usually created because of the
  236.      * user is having a chat session with this contact.
  237.      *
  238.      * @param address
  239.      *            the address of the contact.
  240.      * @return <code>true</code> if it's a temporary contact;
  241.      *         <code>false</code> otherwise.
  242.      */
  243.     public boolean isTemporary(String address) {
  244.         synchronized (mTemporaryContacts) {
  245.             return mTemporaryContacts.containsKey(address);
  246.         }
  247.     }
  248.     ContactListAdapter getContactListAdapter(String name) {
  249.         synchronized (mContactLists) {
  250.             for (ContactListAdapter list : mContactLists.values()) {
  251.                 if (name.equals(list.getName())) {
  252.                     return list;
  253.                 }
  254.             }
  255.             return null;
  256.         }
  257.     }
  258.     ContactListAdapter getContactListAdapter(Address address) {
  259.         synchronized (mContactLists) {
  260.             return mContactLists.get(address);
  261.         }
  262.     }
  263.     private class Exclusion {
  264.         private StringBuilder mSelection;
  265.         private List mSelectionArgs;
  266.         private String mExclusionColumn;
  267.         Exclusion(String exclusionColumn, Collection<String> items) {
  268.             mSelection = new StringBuilder();
  269.             mSelectionArgs = new ArrayList();
  270.             mExclusionColumn = exclusionColumn;
  271.             for (String s : items) {
  272.                 add(s);
  273.             }
  274.         }
  275.         public void add(String exclusionItem) {
  276.             if (mSelection.length()==0) {
  277.                 mSelection.append(mExclusionColumn + "!=?");
  278.             } else {
  279.                 mSelection.append(" AND " + mExclusionColumn + "!=?");
  280.             }
  281.             mSelectionArgs.add(exclusionItem);
  282.         }
  283.         public String getSelection() {
  284.             return mSelection.toString();
  285.         }
  286.         public String[] getSelectionArgs() {
  287.             return (String []) mSelectionArgs.toArray(new String[0]);
  288.         }
  289.     }
  290.     private void removeObsoleteContactsAndLists() {
  291.         // remove all contacts for this provider & account which have not been
  292.         // added since login, yet still exist in db from a prior login
  293.         Exclusion exclusion = new Exclusion(Im.Contacts.USERNAME, mValidatedContacts);
  294.         mResolver.delete(mContactUrl, exclusion.getSelection(), exclusion.getSelectionArgs());
  295.         // remove all blocked contacts for this provider & account which have not been
  296.         // added since login, yet still exist in db from a prior login
  297.         exclusion = new Exclusion(Im.BlockedList.USERNAME, mValidatedBlockedContacts);
  298.         Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
  299.         ContentUris.appendId(builder, mProviderId);
  300.         ContentUris.appendId(builder, mAccountId);
  301.         Uri uri = builder.build();
  302.         mResolver.delete(uri, exclusion.getSelection(), exclusion.getSelectionArgs());
  303.         // remove all contact lists for this provider & account which have not been
  304.         // added since login, yet still exist in db from a prior login
  305.         exclusion = new Exclusion(Im.ContactList.NAME, mValidatedContactLists);
  306.         builder = Im.ContactList.CONTENT_URI.buildUpon();
  307.         ContentUris.appendId(builder, mProviderId);
  308.         ContentUris.appendId(builder, mAccountId);
  309.         uri = builder.build();
  310.         mResolver.delete(uri, exclusion.getSelection(), exclusion.getSelectionArgs());
  311.     }
  312.     final class ContactListListenerAdapter
  313.             extends RemoteListenerManager<IContactListListener>
  314.             implements ContactListListener {
  315.         private boolean mAllContactsLoaded;
  316.         // class to hold contact changes made before mAllContactsLoaded
  317.         private class StoredContactChange {
  318.             int mType;
  319.             ContactList mList;
  320.             Contact mContact;
  321.             StoredContactChange(int type, ContactList list, Contact contact) {
  322.                 mType = type;
  323.                 mList = list;
  324.                 mContact = contact;
  325.             }
  326.         }
  327.         private Vector<StoredContactChange> mDelayedContactChanges =
  328.                 new Vector<StoredContactChange>();
  329.         public void onContactsPresenceUpdate(final Contact[] contacts) {
  330.             // The client listens only to presence updates for now. Update
  331.             // the avatars first to ensure it can get the new avatar when
  332.             // presence updated.
  333.             // TODO: Don't update avatar now since none of the server supports it
  334.             // updateAvatarsContent(contacts);
  335.             updatePresenceContent(contacts);
  336.             notifyRemoteListeners(new ListenerInvocation<IContactListListener>() {
  337.                 public void invoke(IContactListListener remoteListener)
  338.                         throws RemoteException {
  339.                     remoteListener.onContactsPresenceUpdate(contacts);
  340.                 }
  341.             });
  342.         }
  343.         public void onContactChange(final int type, final ContactList list,
  344.                 final Contact contact) {
  345.             ContactListAdapter removed = null;
  346.             String notificationText = null;
  347.             switch (type) {
  348.             case LIST_LOADED:
  349.             case LIST_CREATED:
  350.                 addContactListContent(list);
  351.                 break;
  352.             case LIST_DELETED:
  353.                 removed = removeContactListFromDataBase(list.getName());
  354.                 // handle case where a list is deleted before mAllContactsLoaded
  355.                 if (!mAllContactsLoaded) {
  356.                     // if a cached contact list is deleted before the actual contact list is
  357.                     // downloaded from the server, we will have to remove the list again once
  358.                     // once mAllContactsLoaded is true
  359.                     if (!mValidatedContactLists.contains(list.getName())) {
  360.                         mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
  361.                     }
  362.                 }
  363.                 break;
  364.             case LIST_CONTACT_ADDED:
  365.                 long listId = getContactListAdapter(list.getAddress()).getDataBaseId();
  366.                 String contactAddress = contact.getAddress().getFullName();
  367.                 if(isTemporary(contactAddress)){
  368.                     moveTemporaryContactToList(contactAddress, listId);
  369.                 } else {
  370.                     insertContactContent(contact, listId);
  371.                 }
  372.                 notificationText = mContext.getResources().getString(
  373.                         R.string.add_contact_success, contact.getName());
  374.                 // handle case where a contact is added before mAllContactsLoaded
  375.                 if (!mAllContactsLoaded) {
  376.                     // if a contact is added to a cached contact list before the actual contact
  377.                     // list is downloaded from the server, we will have to add the contact to
  378.                     // the contact list once mAllContactsLoaded is true
  379.                     if (!mValidatedContactLists.contains(list.getName())) {
  380.                         mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
  381.                     }
  382.                 }
  383.                 break;
  384.             case LIST_CONTACT_REMOVED:
  385.                 deleteContactFromDataBase(contact, list);
  386.                 // handle case where a contact is removed before mAllContactsLoaded
  387.                 if (!mAllContactsLoaded) {
  388.                     // if a contact is added to a cached contact list before the actual contact
  389.                     // list is downloaded from the server, we will have to add the contact to
  390.                     // the contact list once mAllContactsLoaded is true
  391.                     if (!mValidatedContactLists.contains(list.getName())) {
  392.                         mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
  393.                     }
  394.                 }
  395.                 // Clear ChatSession if any.
  396.                 String address = contact.getAddress().getFullName();
  397.                 closeChatSession(address);
  398.                 notificationText = mContext.getResources().getString(
  399.                         R.string.delete_contact_success, contact.getName());
  400.                 break;
  401.             case LIST_RENAMED:
  402.                 updateListNameInDataBase(list);
  403.                 // handle case where a list is renamed before mAllContactsLoaded
  404.                 if (!mAllContactsLoaded) {
  405.                     // if a contact list name is updated before the actual contact list is
  406.                     // downloaded from the server, we will have to update the list name again
  407.                     // once mAllContactsLoaded is true
  408.                     if (!mValidatedContactLists.contains(list.getName())) {
  409.                         mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
  410.                     }
  411.                 }
  412.                 break;
  413.             case CONTACT_BLOCKED:
  414.                 insertBlockedContactToDataBase(contact);
  415.                 address = contact.getAddress().getFullName();
  416.                 updateContactType(address, Im.Contacts.TYPE_BLOCKED);
  417.                 closeChatSession(address);
  418.                 notificationText = mContext.getResources().getString(
  419.                         R.string.block_contact_success, contact.getName());
  420.                 break;
  421.             case CONTACT_UNBLOCKED:
  422.                 removeBlockedContactFromDataBase(contact);
  423.                 notificationText = mContext.getResources().getString(
  424.                         R.string.unblock_contact_success, contact.getName());
  425.                 // handle case where a contact is unblocked before mAllContactsLoaded
  426.                 if (!mAllContactsLoaded) {
  427.                     // if a contact list name is updated before the actual contact list is
  428.                     // downloaded from the server, we will have to update the list name again
  429.                     // once mAllContactsLoaded is true
  430.                     if (!mValidatedBlockedContacts.contains(contact.getName())) {
  431.                         mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
  432.                     }
  433.                 }
  434.                 break;
  435.             default:
  436.                 Log.e(TAG, "Unknown list update event!");
  437.                 break;
  438.             }
  439.             final ContactListAdapter listAdapter;
  440.             if (type == LIST_DELETED) {
  441.                 listAdapter = removed;
  442.             } else {
  443.                 listAdapter = (list == null) ? null
  444.                         : getContactListAdapter(list.getAddress());
  445.             }
  446.             notifyRemoteListeners(new ListenerInvocation<IContactListListener>() {
  447.                 public void invoke(IContactListListener remoteListener)
  448.                         throws RemoteException {
  449.                     remoteListener.onContactChange(type, listAdapter, contact);
  450.                 }
  451.             });
  452.             if (mAllContactsLoaded && notificationText != null) {
  453.                 mContext.showToast(notificationText, Toast.LENGTH_SHORT);
  454.             }
  455.         }
  456.         public void onContactError(final int errorType, final ImErrorInfo error,
  457.                 final String listName, final Contact contact) {
  458.             notifyRemoteListeners(new ListenerInvocation<IContactListListener>() {
  459.                 public void invoke(IContactListListener remoteListener)
  460.                         throws RemoteException {
  461.                     remoteListener.onContactError(errorType, error, listName,
  462.                             contact);
  463.                 }
  464.             });
  465.         }
  466.         public void handleDelayedContactChanges() {
  467.             for (StoredContactChange change : mDelayedContactChanges) {
  468.                 onContactChange(change.mType, change.mList, change.mContact);
  469.             }
  470.         }
  471.         public void onAllContactListsLoaded() {
  472.             mAllContactsLoaded = true;
  473.             handleDelayedContactChanges();
  474.             removeObsoleteContactsAndLists();
  475.             notifyRemoteListeners(new ListenerInvocation<IContactListListener>() {
  476.                 public void invoke(IContactListListener remoteListener)
  477.                         throws RemoteException {
  478.                     remoteListener.onAllContactListsLoaded();
  479.                 }
  480.             });
  481.         }
  482.     }
  483.     final class SubscriptionRequestListenerAdapter
  484.         extends RemoteListenerManager<ISubscriptionListener>
  485.         implements SubscriptionRequestListener {
  486.         public void onSubScriptionRequest(final Contact from) {
  487.             String username = from.getAddress().getFullName();
  488.             String nickname = from.getName();
  489.             Uri uri = insertOrUpdateSubscription(username, nickname,
  490.                     Im.Contacts.SUBSCRIPTION_TYPE_FROM,
  491.                     Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING);
  492.             mContext.getStatusBarNotifier().notifySubscriptionRequest(mProviderId, mAccountId,
  493.                     ContentUris.parseId(uri), username, nickname);
  494.             notifyRemoteListeners(new ListenerInvocation<ISubscriptionListener>() {
  495.                 public void invoke(ISubscriptionListener remoteListener)
  496.                         throws RemoteException {
  497.                     remoteListener.onSubScriptionRequest(from);
  498.                 }
  499.             });
  500.         }
  501.         public void onSubscriptionApproved(final String contact) {
  502.             insertOrUpdateSubscription(contact, null,
  503.                     Im.Contacts.SUBSCRIPTION_TYPE_NONE,
  504.                     Im.Contacts.SUBSCRIPTION_STATUS_NONE);
  505.             notifyRemoteListeners(new ListenerInvocation<ISubscriptionListener>() {
  506.                 public void invoke(ISubscriptionListener remoteListener)
  507.                         throws RemoteException {
  508.                     remoteListener.onSubscriptionApproved(contact);
  509.                 }
  510.             });
  511.         }
  512.         public void onSubscriptionDeclined(final String contact) {
  513.             insertOrUpdateSubscription(contact, null,
  514.                     Im.Contacts.SUBSCRIPTION_TYPE_NONE,
  515.                     Im.Contacts.SUBSCRIPTION_STATUS_NONE);
  516.             notifyRemoteListeners(new ListenerInvocation<ISubscriptionListener>() {
  517.                 public void invoke(ISubscriptionListener remoteListener)
  518.                         throws RemoteException {
  519.                     remoteListener.onSubscriptionDeclined(contact);
  520.                 }
  521.             });
  522.         }
  523.         public void onApproveSubScriptionError(final String contact, final ImErrorInfo error) {
  524.             String displayableAddress = getDisplayableAddress(contact);
  525.             String msg = mContext.getString(R.string.approve_subscription_error, displayableAddress);
  526.             mContext.showToast(msg, Toast.LENGTH_SHORT);
  527.         }
  528.         public void onDeclineSubScriptionError(final String contact, final ImErrorInfo error) {
  529.             String displayableAddress = getDisplayableAddress(contact);
  530.             String msg = mContext.getString(R.string.decline_subscription_error, displayableAddress);
  531.             mContext.showToast(msg, Toast.LENGTH_SHORT);
  532.         }
  533.     }
  534.     String getDisplayableAddress(String impsAddress) {
  535.         if (impsAddress.startsWith("wv:")) {
  536.             return impsAddress.substring(3);
  537.         }
  538.         return impsAddress;
  539.     }
  540.     void insertBlockedContactToDataBase(Contact contact) {
  541.         // Remove the blocked contact if it already exists, to avoid duplicates and
  542.         // handle the odd case where a blocked contact's nickname has changed
  543.         removeBlockedContactFromDataBase(contact);
  544.         Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
  545.         ContentUris.appendId(builder, mProviderId);
  546.         ContentUris.appendId(builder, mAccountId);
  547.         Uri uri = builder.build();
  548.         String username = contact.getAddress().getFullName();
  549.         ContentValues values = new ContentValues(2);
  550.         values.put(Im.BlockedList.USERNAME, username);
  551.         values.put(Im.BlockedList.NICKNAME, contact.getName());
  552.         mResolver.insert(uri, values);
  553.         mValidatedBlockedContacts.add(username);
  554.     }
  555.     void removeBlockedContactFromDataBase(Contact contact) {
  556.         String address = contact.getAddress().getFullName();
  557.         Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
  558.         ContentUris.appendId(builder, mProviderId);
  559.         ContentUris.appendId(builder, mAccountId);
  560.         Uri uri = builder.build();
  561.         mResolver.delete(uri, Im.BlockedList.USERNAME + "=?", new String[]{ address });
  562.         int type = isTemporary(address) ? Im.Contacts.TYPE_TEMPORARY
  563.                 : Im.Contacts.TYPE_NORMAL;
  564.         updateContactType(address, type);
  565.     }
  566.     void moveTemporaryContactToList(String address, long listId) {
  567.         synchronized (mTemporaryContacts) {
  568.             mTemporaryContacts.remove(address);
  569.         }
  570.         ContentValues values = new ContentValues(2);
  571.         values.put(Im.Contacts.TYPE, Im.Contacts.TYPE_NORMAL);
  572.         values.put(Im.Contacts.CONTACTLIST, listId);
  573.         String selection = Im.Contacts.USERNAME + "=? AND " + Im.Contacts.TYPE + "="
  574.                 + Im.Contacts.TYPE_TEMPORARY;
  575.         String[] selectionArgs = { address };
  576.         mResolver.update(mContactUrl, values, selection, selectionArgs);
  577.     }
  578.     void updateContactType(String address, int type) {
  579.         ContentValues values = new ContentValues(1);
  580.         values.put(Im.Contacts.TYPE, type);
  581.         updateContact(address, values);
  582.     }
  583.     /**
  584.      * Insert or update subscription request from user into the database.
  585.      *
  586.      * @param username
  587.      * @param nickname
  588.      * @param subscriptionType
  589.      * @param subscriptionStatus
  590.      */
  591.     Uri insertOrUpdateSubscription(String username, String nickname, int subscriptionType,
  592.             int subscriptionStatus) {
  593.         Cursor cursor = mResolver.query(mContactUrl, new String[]{ Im.Contacts._ID },
  594.                 Im.Contacts.USERNAME + "=?", new String[]{username}, null);
  595.         if (cursor == null) {
  596.             Log.w(TAG, "query contact " + username + " failed");
  597.             return null;
  598.         }
  599.         Uri uri;
  600.         if (cursor.moveToFirst()) {
  601.             ContentValues values = new ContentValues(2);
  602.             values.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
  603.             values.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
  604.             long contactId = cursor.getLong(0);
  605.             uri = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, contactId);
  606.             mResolver.update(uri, values, null, null);
  607.         } else {
  608.             ContentValues values = new ContentValues(6);
  609.             values.put(Im.Contacts.USERNAME, username);
  610.             values.put(Im.Contacts.NICKNAME, nickname);
  611.             values.put(Im.Contacts.TYPE, Im.Contacts.TYPE_NORMAL);
  612.             values.put(Im.Contacts.CONTACTLIST, FAKE_TEMPORARY_LIST_ID);
  613.             values.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
  614.             values.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
  615.             uri = mResolver.insert(mContactUrl, values);
  616.         }
  617.         cursor.close();
  618.         return uri;
  619.     }
  620.     void updateContact(String username, ContentValues values) {
  621.         String selection = Im.Contacts.USERNAME + "=?";
  622.         String[] selectionArgs = { username };
  623.         mResolver.update(mContactUrl, values, selection, selectionArgs);
  624.     }
  625.     void updatePresenceContent(Contact[] contacts) {
  626.         ArrayList<String> usernames = new ArrayList<String>();
  627.         ArrayList<String> statusArray = new ArrayList<String>();
  628.         ArrayList<String> customStatusArray = new ArrayList<String>();
  629.         ArrayList<String> clientTypeArray = new ArrayList<String>();
  630.         for(Contact c : contacts) {
  631.             String username = c.getAddress().getFullName();
  632.             Presence p = c.getPresence();
  633.             int status = getContactStatus(p);
  634.             String customStatus = p.getStatusText();
  635.             int clientType = translateClientType(p);
  636.             usernames.add(username);
  637.             statusArray.add(String.valueOf(status));
  638.             customStatusArray.add(customStatus);
  639.             clientTypeArray.add(String.valueOf(clientType));
  640.         }
  641.         ContentValues values = new ContentValues();
  642.         values.put(Im.Contacts.ACCOUNT, mAccountId);
  643.         values.putStringArrayList(Im.Contacts.USERNAME, usernames);
  644.         values.putStringArrayList(Im.Presence.PRESENCE_STATUS, statusArray);
  645.         values.putStringArrayList(Im.Presence.PRESENCE_CUSTOM_STATUS, customStatusArray);
  646.         values.putStringArrayList(Im.Presence.CONTENT_TYPE, clientTypeArray);
  647.         mResolver.update(Im.Presence.BULK_CONTENT_URI, values, null, null);
  648.     }
  649.     void updateAvatarsContent(Contact[] contacts) {
  650.         ArrayList<ContentValues> avatars = new ArrayList<ContentValues>();
  651.         ArrayList<String> usernames = new ArrayList<String>();
  652.         for (Contact contact : contacts) {
  653.             byte[] avatarData = contact.getPresence().getAvatarData();
  654.             if (avatarData == null) {
  655.                 continue;
  656.             }
  657.             String username = contact.getAddress().getFullName();
  658.             ContentValues values = new ContentValues(2);
  659.             values.put(Im.Avatars.CONTACT, username);
  660.             values.put(Im.Avatars.DATA, avatarData);
  661.             avatars.add(values);
  662.             usernames.add(username);
  663.         }
  664.         if (avatars.size() > 0) {
  665.             // ImProvider will replace the avatar content if it already exist.
  666.             mResolver.bulkInsert(mAvatarUrl, avatars.toArray(
  667.                     new ContentValues[avatars.size()]));
  668.             // notify avatar changed
  669.             Intent i = new Intent(ImServiceConstants.ACTION_AVATAR_CHANGED);
  670.             i.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, usernames);
  671.             i.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId);
  672.             i.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
  673.             mContext.sendBroadcast(i);
  674.         }
  675.     }
  676.     ContactListAdapter removeContactListFromDataBase(String name) {
  677.         ContactListAdapter listAdapter = getContactListAdapter(name);
  678.         if (listAdapter == null) {
  679.             return null;
  680.         }
  681.         long id = listAdapter.getDataBaseId();
  682.         // delete contacts of this list first
  683.         mResolver.delete(mContactUrl,
  684.             Im.Contacts.CONTACTLIST + "=?", new String[]{Long.toString(id)});
  685.         mResolver.delete(ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, id), null, null);
  686.         synchronized (mContactLists) {
  687.             return mContactLists.remove(listAdapter.getAddress());
  688.         }
  689.     }
  690.     void addContactListContent(ContactList list) {
  691.         String selection = Im.ContactList.NAME + "=? AND "
  692.                 + Im.ContactList.PROVIDER + "=? AND "
  693.                 + Im.ContactList.ACCOUNT + "=?";
  694.         String[] selectionArgs = { list.getName(),
  695.                 Long.toString(mProviderId),
  696.                 Long.toString(mAccountId) };
  697.         Cursor cursor = mResolver.query(Im.ContactList.CONTENT_URI,
  698.                                         CONTACT_LIST_ID_PROJECTION,
  699.                                         selection,
  700.                                         selectionArgs,
  701.                                         null); // no sort order
  702.         long listId = 0;
  703.         Uri uri = null;
  704.         try {
  705.             if (cursor.moveToFirst()) {
  706.                 listId = cursor.getLong(0);
  707.                 uri = ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, listId);
  708.                 //Log.d(TAG,"Found and removing ContactList with name "+list.getName());
  709.             }
  710.         } finally {
  711.             cursor.close();
  712.         }
  713.         if (uri != null) {
  714.             // remove existing ContactList and Contacts of that list for replacement by the newly
  715.             // downloaded list
  716.             mResolver.delete(mContactUrl, Im.Contacts.CONTACTLIST + "=?",
  717.                     new String[]{Long.toString(listId)});
  718.             mResolver.delete(uri, selection, selectionArgs);
  719.         }
  720.         ContentValues contactListValues = new ContentValues(3);
  721.         contactListValues.put(Im.ContactList.NAME, list.getName());
  722.         contactListValues.put(Im.ContactList.PROVIDER, mProviderId);
  723.         contactListValues.put(Im.ContactList.ACCOUNT, mAccountId);
  724.         //Log.d(TAG, "Adding ContactList name="+list.getName());
  725.         mValidatedContactLists.add(list.getName());
  726.         uri = mResolver.insert(Im.ContactList.CONTENT_URI, contactListValues);
  727.         listId = ContentUris.parseId(uri);
  728.         synchronized (mContactLists) {
  729.             mContactLists.put(list.getAddress(),
  730.                     new ContactListAdapter(list, listId));
  731.         }
  732.         Collection<Contact> contacts = list.getContacts();
  733.         if (contacts == null || contacts.size() == 0) {
  734.             return;
  735.         }
  736.         Iterator<Contact> iter = contacts.iterator();
  737.         while(iter.hasNext()) {
  738.             Contact c = iter.next();
  739.             String address = c.getAddress().getFullName();
  740.             if(isTemporary(address)) {
  741.                 moveTemporaryContactToList(address, listId);
  742.                 iter.remove();
  743.             }
  744.             mValidatedContacts.add(address);
  745.         }
  746.         ArrayList<String> usernames = new ArrayList<String>();
  747.         ArrayList<String> nicknames = new ArrayList<String>();
  748.         ArrayList<String> contactTypeArray = new ArrayList<String>();
  749.         for (Contact c : contacts) {
  750.             String username = c.getAddress().getFullName();
  751.             String nickname = c.getName();
  752.             int type = Im.Contacts.TYPE_NORMAL;
  753.             if(isTemporary(username)) {
  754.                 type = Im.Contacts.TYPE_TEMPORARY;
  755.             }
  756.             if (isBlocked(username)) {
  757.                 type = Im.Contacts.TYPE_BLOCKED;
  758.             }
  759.             usernames.add(username);
  760.             nicknames.add(nickname);
  761.             contactTypeArray.add(String.valueOf(type));
  762.         }
  763.         ContentValues values = new ContentValues(6);
  764.         values.put(Im.Contacts.PROVIDER, mProviderId);
  765.         values.put(Im.Contacts.ACCOUNT, mAccountId);
  766.         values.put(Im.Contacts.CONTACTLIST, listId);
  767.         values.putStringArrayList(Im.Contacts.USERNAME, usernames);
  768.         values.putStringArrayList(Im.Contacts.NICKNAME, nicknames);
  769.         values.putStringArrayList(Im.Contacts.TYPE, contactTypeArray);
  770.         mResolver.insert(Im.Contacts.BULK_CONTENT_URI, values);
  771.     }
  772.     void updateListNameInDataBase(ContactList list) {
  773.         ContactListAdapter listAdapter = getContactListAdapter(list.getAddress());
  774.         Uri uri = ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, listAdapter.getDataBaseId());
  775.         ContentValues values = new ContentValues(1);
  776.         values.put(Im.ContactList.NAME, list.getName());
  777.         mResolver.update(uri, values, null, null);
  778.     }
  779.     void deleteContactFromDataBase(Contact contact, ContactList list) {
  780.         String selection = Im.Contacts.USERNAME
  781.                 + "=? AND " + Im.Contacts.CONTACTLIST + "=?";
  782.         long listId = getContactListAdapter(list.getAddress()).getDataBaseId();
  783.         String username = contact.getAddress().getFullName();
  784.         String[] selectionArgs = {username, Long.toString(listId)};
  785.         mResolver.delete(mContactUrl, selection, selectionArgs);
  786.         // clear the history message if the contact doesn't exist in any list
  787.         // anymore.
  788.         if(mAdaptee.getContact(contact.getAddress()) == null) {
  789.             clearHistoryMessages(username);
  790.         }
  791.     }
  792.     Uri insertContactContent(Contact contact, long listId) {
  793.         ContentValues values = getContactContentValues(contact, listId);
  794.         Uri uri = mResolver.insert(mContactUrl, values);
  795.         ContentValues presenceValues = getPresenceValues(ContentUris.parseId(uri),
  796.                 contact.getPresence());
  797.         mResolver.insert(Im.Presence.CONTENT_URI, presenceValues);
  798.         return uri;
  799.     }
  800.     private ContentValues getContactContentValues(Contact contact, long listId) {
  801.         final String username = contact.getAddress().getFullName();
  802.         final String nickname = contact.getName();
  803.         int type = Im.Contacts.TYPE_NORMAL;
  804.         if(isTemporary(username)) {
  805.             type = Im.Contacts.TYPE_TEMPORARY;
  806.         }
  807.         if (isBlocked(username)) {
  808.             type = Im.Contacts.TYPE_BLOCKED;
  809.         }
  810.         ContentValues values = new ContentValues(4);
  811.         values.put(Im.Contacts.USERNAME, username);
  812.         values.put(Im.Contacts.NICKNAME, nickname);
  813.         values.put(Im.Contacts.CONTACTLIST, listId);
  814.         values.put(Im.Contacts.TYPE, type);
  815.         return values;
  816.     }
  817.     void clearHistoryMessages(String contact) {
  818.         Uri uri = Im.Messages.getContentUriByContact(mProviderId,
  819.             mAccountId, contact);
  820.         mResolver.delete(uri, null, null);
  821.     }
  822.     private ContentValues getPresenceValues(long contactId, Presence p) {
  823.         ContentValues values = new ContentValues(3);
  824.         values.put(Im.Presence.CONTACT_ID, contactId);
  825.         values.put(Im.Contacts.PRESENCE_STATUS, getContactStatus(p));
  826.         values.put(Im.Contacts.PRESENCE_CUSTOM_STATUS, p.getStatusText());
  827.         values.put(Im.Presence.CLIENT_TYPE, translateClientType(p));
  828.         return values;
  829.     }
  830.     private int translateClientType(Presence presence) {
  831.         int clientType = presence.getClientType();
  832.         switch (clientType) {
  833.             case Presence.CLIENT_TYPE_MOBILE:
  834.                 return Im.Presence.CLIENT_TYPE_MOBILE;
  835.             default:
  836.                 return Im.Presence.CLIENT_TYPE_DEFAULT;
  837.         }
  838.     }
  839.     private int getContactStatus(Presence presence) {
  840.         switch (presence.getStatus()) {
  841.         case Presence.AVAILABLE:
  842.             return Im.Presence.AVAILABLE;
  843.         case Presence.IDLE:
  844.             return Im.Presence.IDLE;
  845.         case Presence.AWAY:
  846.             return Im.Presence.AWAY;
  847.         case Presence.DO_NOT_DISTURB:
  848.             return Im.Presence.DO_NOT_DISTURB;
  849.         case Presence.OFFLINE:
  850.             return Im.Presence.OFFLINE;
  851.         }
  852.         // impossible...
  853.         Log.e(TAG, "Illegal presence status value " + presence.getStatus());
  854.         return Im.Presence.AVAILABLE;
  855.     }
  856.     public void clearOnLogout() {
  857.         clearValidatedContactsAndLists();
  858.         clearTemporaryContacts();
  859.         clearPresence();
  860.     }
  861.     /**
  862.      * Clears the list of validated contacts and contact lists.
  863.      * As contacts and contacts lists are added after login, contacts and contact lists are
  864.      * stored as "validated contacts". After initial download of contacts is complete, any contacts
  865.      * and contact lists that remain in the database, but are not in the validated list, are
  866.      * obsolete and should be removed.  This function resets that list for use upon login.
  867.      */
  868.     private void clearValidatedContactsAndLists() {
  869.         // clear the list of validated contacts, contact lists, and blocked contacts
  870.         mValidatedContacts.clear();
  871.         mValidatedContactLists.clear();
  872.         mValidatedBlockedContacts.clear();
  873.     }
  874.     /**
  875.      * Clear the temporary contacts in the database. As contacts are persist between
  876.      * IM sessions, the temporary contacts need to be cleared after logout.
  877.      */
  878.     private void clearTemporaryContacts() {
  879.         String selection = Im.Contacts.CONTACTLIST + "=" + FAKE_TEMPORARY_LIST_ID;
  880.         mResolver.delete(mContactUrl, selection, null);
  881.     }
  882.     /**
  883.      * Clears the presence of the all contacts. As contacts are persist between
  884.      * IM sessions, the presence need to be cleared after logout.
  885.      */
  886.     void clearPresence() {
  887.         StringBuilder where = new StringBuilder();
  888.         where.append(Im.Presence.CONTACT_ID);
  889.         where.append(" in (select _id from contacts where ");
  890.         where.append(Im.Contacts.ACCOUNT);
  891.         where.append("=");
  892.         where.append(mAccountId);
  893.         where.append(")");
  894.         mResolver.delete(Im.Presence.CONTENT_URI, where.toString(), null);
  895.     }
  896.     void closeChatSession(String address) {
  897.         ChatSessionManagerAdapter sessionManager =
  898.             (ChatSessionManagerAdapter) mConn.getChatSessionManager();
  899.         ChatSessionAdapter session =
  900.             (ChatSessionAdapter) sessionManager.getChatSession(address);
  901.         if(session != null) {
  902.             session.leave();
  903.         }
  904.     }
  905.     void updateChatPresence(String address, String nickname, Presence p) {
  906.         ChatSessionManagerAdapter sessionManager =
  907.             (ChatSessionManagerAdapter) mConn.getChatSessionManager();
  908.         // TODO: This only find single chat sessions, we need to go through all
  909.         // active chat sessions and find if the contact is a participant of the
  910.         // session.
  911.         ChatSessionAdapter session =
  912.             (ChatSessionAdapter) sessionManager.getChatSession(address);
  913.         if(session != null) {
  914.             session.insertPresenceUpdatesMsg(nickname, p);
  915.         }
  916.     }
  917. }