blob: 47c1615fc0205320d6fa6f5188fe69dac76deb39 [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.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
import com.android.im.IChatListener;
import com.android.im.IContactList;
import com.android.im.IContactListListener;
import com.android.im.IContactListManager;
import com.android.im.ISubscriptionListener;
import com.android.im.R;
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.ImErrorInfo;
import com.android.im.engine.ImException;
import com.android.im.engine.Presence;
import com.android.im.engine.SubscriptionRequestListener;
import com.android.im.provider.Imps;
public class ContactListManagerAdapter extends IContactListManager.Stub {
static final String TAG = RemoteImService.TAG;
ImConnectionAdapter mConn;
ContentResolver mResolver;
private ContactListManager mAdaptee;
private ContactListListenerAdapter mContactListListenerAdapter;
private SubscriptionRequestListenerAdapter mSubscriptionListenerAdapter;
final RemoteCallbackList<IContactListListener> mRemoteContactListeners
= new RemoteCallbackList<IContactListListener>();
final RemoteCallbackList<ISubscriptionListener> mRemoteSubscriptionListeners
= new RemoteCallbackList<ISubscriptionListener>();
HashMap<Address, ContactListAdapter> mContactLists;
HashMap<String, Contact> mTemporaryContacts;
HashSet<String> mValidatedContactLists;
HashSet<String> mValidatedContacts;
HashSet<String> mValidatedBlockedContacts;
private long mAccountId;
private long mProviderId;
private Uri mAvatarUrl;
private Uri mContactUrl;
static final long FAKE_TEMPORARY_LIST_ID = -1;
static final String[] CONTACT_LIST_ID_PROJECTION = { Imps.ContactList._ID };
RemoteImService mContext;
public ContactListManagerAdapter(ImConnectionAdapter conn) {
mAdaptee = conn.getAdaptee().getContactListManager();
mConn = conn;
mContext = conn.getContext();
mResolver = mContext.getContentResolver();
mContactListListenerAdapter = new ContactListListenerAdapter();
mSubscriptionListenerAdapter = new SubscriptionRequestListenerAdapter();
mContactLists = new HashMap<Address, ContactListAdapter>();
mTemporaryContacts = new HashMap<String, Contact>();
mValidatedContacts = new HashSet<String>();
mValidatedContactLists = new HashSet<String>();
mValidatedBlockedContacts = new HashSet<String>();
mAdaptee.addContactListListener(mContactListListenerAdapter);
mAdaptee.setSubscriptionRequestListener(mSubscriptionListenerAdapter);
mAccountId = mConn.getAccountId();
mProviderId = mConn.getProviderId();
Uri.Builder builder = Imps.Avatars.CONTENT_URI_AVATARS_BY.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
mAvatarUrl = builder.build();
builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
mContactUrl = builder.build();
}
public int createContactList(String name, List<Contact> contacts) {
try {
mAdaptee.createContactListAsync(name, contacts);
} catch (ImException e) {
return e.getImError().getCode();
}
return ImErrorInfo.NO_ERROR;
}
public int deleteContactList(String name) {
try {
mAdaptee.deleteContactListAsync(name);
} catch (ImException e) {
return e.getImError().getCode();
}
return ImErrorInfo.NO_ERROR;
}
public List getContactLists() {
synchronized (mContactLists) {
return new ArrayList<ContactListAdapter>(mContactLists.values());
}
}
public int removeContact(String address) {
if(isTemporary(address)) {
// For temporary contact, just close the session and delete him in
// database.
closeChatSession(address);
String selection = Imps.Contacts.USERNAME + "=?";
String[] selectionArgs = { address };
mResolver.delete(mContactUrl, selection, selectionArgs);
synchronized (mTemporaryContacts) {
mTemporaryContacts.remove(address);
}
} else {
synchronized (mContactLists) {
for(ContactListAdapter list : mContactLists.values()) {
int resCode = list.removeContact(address);
if (ImErrorInfo.ILLEGAL_CONTACT_ADDRESS == resCode) {
// Did not find in this list, continue to remove from
// other list.
continue;
}
if (ImErrorInfo.NO_ERROR != resCode) {
return resCode;
}
}
}
}
return ImErrorInfo.NO_ERROR;
}
public void approveSubscription(String address) {
mAdaptee.approveSubscriptionRequest(address);
}
public void declineSubscription(String address) {
mAdaptee.declineSubscriptionRequest(address);
}
public int blockContact(String address) {
try {
mAdaptee.blockContactAsync(address);
} catch (ImException e) {
return e.getImError().getCode();
}
return ImErrorInfo.NO_ERROR;
}
public int unBlockContact(String address) {
try {
mAdaptee.unblockContactAsync(address);
} catch (ImException e) {
Log.e(TAG, e.getMessage());
return e.getImError().getCode();
}
return ImErrorInfo.NO_ERROR;
}
public boolean isBlocked(String address) {
try {
return mAdaptee.isBlocked(address);
} catch (ImException e) {
Log.e(TAG, e.getMessage());
return false;
}
}
public void registerContactListListener(IContactListListener listener) {
if (listener != null) {
mRemoteContactListeners.register(listener);
}
}
public void unregisterContactListListener(IContactListListener listener) {
if (listener != null) {
mRemoteContactListeners.unregister(listener);
}
}
public void registerSubscriptionListener(ISubscriptionListener listener) {
if (listener != null) {
mRemoteSubscriptionListeners.register(listener);
}
}
public void unregisterSubscriptionListener(ISubscriptionListener listener) {
if (listener != null) {
mRemoteSubscriptionListeners.unregister(listener);
}
}
public IContactList getContactList(String name) {
return getContactListAdapter(name);
}
public void loadContactLists() {
if(mAdaptee.getState() == ContactListManager.LISTS_NOT_LOADED){
clearValidatedContactsAndLists();
mAdaptee.loadContactListsAsync();
}
}
public int getState() {
return mAdaptee.getState();
}
public Contact getContactByAddress(String address) {
Contact c = mAdaptee.getContact(address);
if(c == null) {
synchronized (mTemporaryContacts) {
return mTemporaryContacts.get(address);
}
} else {
return c;
}
}
public Contact createTemporaryContact(String address) {
Contact c = mAdaptee.createTemporaryContact(address);
insertTemporary(c);
return c;
}
public long queryOrInsertContact(Contact c) {
long result;
String username = c.getAddress().getFullName();
String selection = Imps.Contacts.USERNAME + "=?";
String[] selectionArgs = { username };
String[] projection = {Imps.Contacts._ID};
Cursor cursor = mResolver.query(mContactUrl, projection, selection,
selectionArgs, null);
if(cursor != null && cursor.moveToFirst()) {
result = cursor.getLong(0);
} else {
result = insertTemporary(c);
}
if(cursor != null) {
cursor.close();
}
return result;
}
private long insertTemporary(Contact c) {
synchronized (mTemporaryContacts) {
mTemporaryContacts.put(c.getAddress().getFullName(), c);
}
Uri uri = insertContactContent(c, FAKE_TEMPORARY_LIST_ID);
return ContentUris.parseId(uri);
}
/**
* Tells if a contact is a temporary one which is not in the list of
* contacts that we subscribe presence for. Usually created because of the
* user is having a chat session with this contact.
*
* @param address
* the address of the contact.
* @return <code>true</code> if it's a temporary contact;
* <code>false</code> otherwise.
*/
public boolean isTemporary(String address) {
synchronized (mTemporaryContacts) {
return mTemporaryContacts.containsKey(address);
}
}
ContactListAdapter getContactListAdapter(String name) {
synchronized (mContactLists) {
for (ContactListAdapter list : mContactLists.values()) {
if (name.equals(list.getName())) {
return list;
}
}
return null;
}
}
ContactListAdapter getContactListAdapter(Address address) {
synchronized (mContactLists) {
return mContactLists.get(address);
}
}
private class Exclusion {
private StringBuilder mSelection;
private List mSelectionArgs;
private String mExclusionColumn;
Exclusion(String exclusionColumn, Collection<String> items) {
mSelection = new StringBuilder();
mSelectionArgs = new ArrayList();
mExclusionColumn = exclusionColumn;
for (String s : items) {
add(s);
}
}
public void add(String exclusionItem) {
if (mSelection.length()==0) {
mSelection.append(mExclusionColumn + "!=?");
} else {
mSelection.append(" AND " + mExclusionColumn + "!=?");
}
mSelectionArgs.add(exclusionItem);
}
public String getSelection() {
return mSelection.toString();
}
public String[] getSelectionArgs() {
return (String []) mSelectionArgs.toArray(new String[0]);
}
}
private void removeObsoleteContactsAndLists() {
// remove all contacts for this provider & account which have not been
// added since login, yet still exist in db from a prior login
Exclusion exclusion = new Exclusion(Imps.Contacts.USERNAME, mValidatedContacts);
mResolver.delete(mContactUrl, exclusion.getSelection(), exclusion.getSelectionArgs());
// remove all blocked contacts for this provider & account which have not been
// added since login, yet still exist in db from a prior login
exclusion = new Exclusion(Imps.BlockedList.USERNAME, mValidatedBlockedContacts);
Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
Uri uri = builder.build();
mResolver.delete(uri, exclusion.getSelection(), exclusion.getSelectionArgs());
// remove all contact lists for this provider & account which have not been
// added since login, yet still exist in db from a prior login
exclusion = new Exclusion(Imps.ContactList.NAME, mValidatedContactLists);
builder = Imps.ContactList.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
uri = builder.build();
mResolver.delete(uri, exclusion.getSelection(), exclusion.getSelectionArgs());
}
final class ContactListListenerAdapter implements ContactListListener {
private boolean mAllContactsLoaded;
// class to hold contact changes made before mAllContactsLoaded
private class StoredContactChange {
int mType;
ContactList mList;
Contact mContact;
StoredContactChange(int type, ContactList list, Contact contact) {
mType = type;
mList = list;
mContact = contact;
}
}
private Vector<StoredContactChange> mDelayedContactChanges =
new Vector<StoredContactChange>();
public void onContactsPresenceUpdate(final Contact[] contacts) {
// The client listens only to presence updates for now. Update
// the avatars first to ensure it can get the new avatar when
// presence updated.
// TODO: Don't update avatar now since none of the server supports it
// updateAvatarsContent(contacts);
updatePresenceContent(contacts);
final int N = mRemoteContactListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IContactListListener listener =
mRemoteContactListeners.getBroadcastItem(i);
try {
listener.onContactsPresenceUpdate(contacts);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteContactListeners.finishBroadcast();
}
public void onContactChange(final int type, final ContactList list,
final Contact contact) {
ContactListAdapter removed = null;
String notificationText = null;
switch (type) {
case LIST_LOADED:
case LIST_CREATED:
addContactListContent(list);
break;
case LIST_DELETED:
removed = removeContactListFromDataBase(list.getName());
// handle case where a list is deleted before mAllContactsLoaded
if (!mAllContactsLoaded) {
// if a cached contact list is deleted before the actual contact list is
// downloaded from the server, we will have to remove the list again once
// once mAllContactsLoaded is true
if (!mValidatedContactLists.contains(list.getName())) {
mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
}
}
break;
case LIST_CONTACT_ADDED:
long listId = getContactListAdapter(list.getAddress()).getDataBaseId();
String contactAddress = contact.getAddress().getFullName();
if(isTemporary(contactAddress)){
moveTemporaryContactToList(contactAddress, listId);
} else {
insertContactContent(contact, listId);
}
notificationText = mContext.getResources().getString(
R.string.add_contact_success, contact.getName());
// handle case where a contact is added before mAllContactsLoaded
if (!mAllContactsLoaded) {
// if a contact is added to a cached contact list before the actual contact
// list is downloaded from the server, we will have to add the contact to
// the contact list once mAllContactsLoaded is true
if (!mValidatedContactLists.contains(list.getName())) {
mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
}
}
break;
case LIST_CONTACT_REMOVED:
deleteContactFromDataBase(contact, list);
// handle case where a contact is removed before mAllContactsLoaded
if (!mAllContactsLoaded) {
// if a contact is added to a cached contact list before the actual contact
// list is downloaded from the server, we will have to add the contact to
// the contact list once mAllContactsLoaded is true
if (!mValidatedContactLists.contains(list.getName())) {
mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
}
}
// Clear ChatSession if any.
String address = contact.getAddress().getFullName();
closeChatSession(address);
notificationText = mContext.getResources().getString(
R.string.delete_contact_success, contact.getName());
break;
case LIST_RENAMED:
updateListNameInDataBase(list);
// handle case where a list is renamed before mAllContactsLoaded
if (!mAllContactsLoaded) {
// if a contact list name is updated before the actual contact list is
// downloaded from the server, we will have to update the list name again
// once mAllContactsLoaded is true
if (!mValidatedContactLists.contains(list.getName())) {
mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
}
}
break;
case CONTACT_BLOCKED:
insertBlockedContactToDataBase(contact);
address = contact.getAddress().getFullName();
updateContactType(address, Imps.Contacts.TYPE_BLOCKED);
closeChatSession(address);
notificationText = mContext.getResources().getString(
R.string.block_contact_success, contact.getName());
break;
case CONTACT_UNBLOCKED:
removeBlockedContactFromDataBase(contact);
notificationText = mContext.getResources().getString(
R.string.unblock_contact_success, contact.getName());
// handle case where a contact is unblocked before mAllContactsLoaded
if (!mAllContactsLoaded) {
// if a contact list name is updated before the actual contact list is
// downloaded from the server, we will have to update the list name again
// once mAllContactsLoaded is true
if (!mValidatedBlockedContacts.contains(contact.getName())) {
mDelayedContactChanges.add(new StoredContactChange(type, list, contact));
}
}
break;
default:
Log.e(TAG, "Unknown list update event!");
break;
}
final ContactListAdapter listAdapter;
if (type == LIST_DELETED) {
listAdapter = removed;
} else {
listAdapter = (list == null) ? null
: getContactListAdapter(list.getAddress());
}
final int N = mRemoteContactListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IContactListListener listener =
mRemoteContactListeners.getBroadcastItem(i);
try {
listener.onContactChange(type, listAdapter, contact);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteContactListeners.finishBroadcast();
if (mAllContactsLoaded && notificationText != null) {
mContext.showToast(notificationText, Toast.LENGTH_SHORT);
}
}
public void onContactError(final int errorType, final ImErrorInfo error,
final String listName, final Contact contact) {
final int N = mRemoteContactListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IContactListListener listener =
mRemoteContactListeners.getBroadcastItem(i);
try {
listener.onContactError(errorType, error, listName, contact);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteContactListeners.finishBroadcast();
}
public void handleDelayedContactChanges() {
for (StoredContactChange change : mDelayedContactChanges) {
onContactChange(change.mType, change.mList, change.mContact);
}
}
public void onAllContactListsLoaded() {
mAllContactsLoaded = true;
handleDelayedContactChanges();
removeObsoleteContactsAndLists();
final int N = mRemoteContactListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IContactListListener listener =
mRemoteContactListeners.getBroadcastItem(i);
try {
listener.onAllContactListsLoaded();
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteContactListeners.finishBroadcast();
}
}
final class SubscriptionRequestListenerAdapter
implements SubscriptionRequestListener {
public void onSubScriptionRequest(final Contact from) {
String username = from.getAddress().getFullName();
String nickname = from.getName();
Uri uri = insertOrUpdateSubscription(username, nickname,
Imps.Contacts.SUBSCRIPTION_TYPE_FROM,
Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING);
mContext.getStatusBarNotifier().notifySubscriptionRequest(mProviderId, mAccountId,
ContentUris.parseId(uri), username, nickname);
final int N = mRemoteSubscriptionListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
ISubscriptionListener listener =
mRemoteSubscriptionListeners.getBroadcastItem(i);
try {
listener.onSubScriptionRequest(from);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteSubscriptionListeners.finishBroadcast();
}
public void onSubscriptionApproved(final String contact) {
insertOrUpdateSubscription(contact, null,
Imps.Contacts.SUBSCRIPTION_TYPE_NONE,
Imps.Contacts.SUBSCRIPTION_STATUS_NONE);
final int N = mRemoteSubscriptionListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
ISubscriptionListener listener =
mRemoteSubscriptionListeners.getBroadcastItem(i);
try {
listener.onSubscriptionApproved(contact);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteSubscriptionListeners.finishBroadcast();
}
public void onSubscriptionDeclined(final String contact) {
insertOrUpdateSubscription(contact, null,
Imps.Contacts.SUBSCRIPTION_TYPE_NONE,
Imps.Contacts.SUBSCRIPTION_STATUS_NONE);
final int N = mRemoteSubscriptionListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
ISubscriptionListener listener =
mRemoteSubscriptionListeners.getBroadcastItem(i);
try {
listener.onSubscriptionDeclined(contact);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing the
// dead listeners.
}
}
mRemoteSubscriptionListeners.finishBroadcast();
}
public void onApproveSubScriptionError(final String contact, final ImErrorInfo error) {
String displayableAddress = getDisplayableAddress(contact);
String msg = mContext.getString(R.string.approve_subscription_error, displayableAddress);
mContext.showToast(msg, Toast.LENGTH_SHORT);
}
public void onDeclineSubScriptionError(final String contact, final ImErrorInfo error) {
String displayableAddress = getDisplayableAddress(contact);
String msg = mContext.getString(R.string.decline_subscription_error, displayableAddress);
mContext.showToast(msg, Toast.LENGTH_SHORT);
}
}
String getDisplayableAddress(String impsAddress) {
if (impsAddress.startsWith("wv:")) {
return impsAddress.substring(3);
}
return impsAddress;
}
void insertBlockedContactToDataBase(Contact contact) {
// Remove the blocked contact if it already exists, to avoid duplicates and
// handle the odd case where a blocked contact's nickname has changed
removeBlockedContactFromDataBase(contact);
Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
Uri uri = builder.build();
String username = contact.getAddress().getFullName();
ContentValues values = new ContentValues(2);
values.put(Imps.BlockedList.USERNAME, username);
values.put(Imps.BlockedList.NICKNAME, contact.getName());
mResolver.insert(uri, values);
mValidatedBlockedContacts.add(username);
}
void removeBlockedContactFromDataBase(Contact contact) {
String address = contact.getAddress().getFullName();
Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, mProviderId);
ContentUris.appendId(builder, mAccountId);
Uri uri = builder.build();
mResolver.delete(uri, Imps.BlockedList.USERNAME + "=?", new String[]{ address });
int type = isTemporary(address) ? Imps.Contacts.TYPE_TEMPORARY
: Imps.Contacts.TYPE_NORMAL;
updateContactType(address, type);
}
void moveTemporaryContactToList(String address, long listId) {
synchronized (mTemporaryContacts) {
mTemporaryContacts.remove(address);
}
ContentValues values = new ContentValues(2);
values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_NORMAL);
values.put(Imps.Contacts.CONTACTLIST, listId);
String selection = Imps.Contacts.USERNAME + "=? AND " + Imps.Contacts.TYPE + "="
+ Imps.Contacts.TYPE_TEMPORARY;
String[] selectionArgs = { address };
mResolver.update(mContactUrl, values, selection, selectionArgs);
}
void updateContactType(String address, int type) {
ContentValues values = new ContentValues(1);
values.put(Imps.Contacts.TYPE, type);
updateContact(address, values);
}
/**
* Insert or update subscription request from user into the database.
*
* @param username
* @param nickname
* @param subscriptionType
* @param subscriptionStatus
*/
Uri insertOrUpdateSubscription(String username, String nickname, int subscriptionType,
int subscriptionStatus) {
Cursor cursor = mResolver.query(mContactUrl, new String[]{ Imps.Contacts._ID },
Imps.Contacts.USERNAME + "=?", new String[]{username}, null);
if (cursor == null) {
Log.w(TAG, "query contact " + username + " failed");
return null;
}
Uri uri;
if (cursor.moveToFirst()) {
ContentValues values = new ContentValues(2);
values.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
values.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
long contactId = cursor.getLong(0);
uri = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, contactId);
mResolver.update(uri, values, null, null);
} else {
ContentValues values = new ContentValues(6);
values.put(Imps.Contacts.USERNAME, username);
values.put(Imps.Contacts.NICKNAME, nickname);
values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_NORMAL);
values.put(Imps.Contacts.CONTACTLIST, FAKE_TEMPORARY_LIST_ID);
values.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
values.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
uri = mResolver.insert(mContactUrl, values);
}
cursor.close();
return uri;
}
void updateContact(String username, ContentValues values) {
String selection = Imps.Contacts.USERNAME + "=?";
String[] selectionArgs = { username };
mResolver.update(mContactUrl, values, selection, selectionArgs);
}
void updatePresenceContent(Contact[] contacts) {
ArrayList<String> usernames = new ArrayList<String>();
ArrayList<String> statusArray = new ArrayList<String>();
ArrayList<String> customStatusArray = new ArrayList<String>();
ArrayList<String> clientTypeArray = new ArrayList<String>();
for(Contact c : contacts) {
String username = c.getAddress().getFullName();
Presence p = c.getPresence();
int status = convertPresenceStatus(p);
String customStatus = p.getStatusText();
int clientType = translateClientType(p);
usernames.add(username);
statusArray.add(String.valueOf(status));
customStatusArray.add(customStatus);
clientTypeArray.add(String.valueOf(clientType));
}
ContentValues values = new ContentValues();
values.put(Imps.Contacts.ACCOUNT, mAccountId);
values.putStringArrayList(Imps.Contacts.USERNAME, usernames);
values.putStringArrayList(Imps.Presence.PRESENCE_STATUS, statusArray);
values.putStringArrayList(Imps.Presence.PRESENCE_CUSTOM_STATUS, customStatusArray);
values.putStringArrayList(Imps.Presence.CONTENT_TYPE, clientTypeArray);
mResolver.update(Imps.Presence.BULK_CONTENT_URI, values, null, null);
}
void updateAvatarsContent(Contact[] contacts) {
ArrayList<ContentValues> avatars = new ArrayList<ContentValues>();
ArrayList<String> usernames = new ArrayList<String>();
for (Contact contact : contacts) {
byte[] avatarData = contact.getPresence().getAvatarData();
if (avatarData == null) {
continue;
}
String username = contact.getAddress().getFullName();
ContentValues values = new ContentValues(2);
values.put(Imps.Avatars.CONTACT, username);
values.put(Imps.Avatars.DATA, avatarData);
avatars.add(values);
usernames.add(username);
}
if (avatars.size() > 0) {
// ImProvider will replace the avatar content if it already exist.
mResolver.bulkInsert(mAvatarUrl, avatars.toArray(
new ContentValues[avatars.size()]));
// notify avatar changed
Intent i = new Intent(ImServiceConstants.ACTION_AVATAR_CHANGED);
i.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, usernames);
i.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId);
i.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
mContext.sendBroadcast(i);
}
}
ContactListAdapter removeContactListFromDataBase(String name) {
ContactListAdapter listAdapter = getContactListAdapter(name);
if (listAdapter == null) {
return null;
}
long id = listAdapter.getDataBaseId();
// delete contacts of this list first
mResolver.delete(mContactUrl,
Imps.Contacts.CONTACTLIST + "=?", new String[]{Long.toString(id)});
mResolver.delete(ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, id), null, null);
synchronized (mContactLists) {
return mContactLists.remove(listAdapter.getAddress());
}
}
void addContactListContent(ContactList list) {
String selection = Imps.ContactList.NAME + "=? AND "
+ Imps.ContactList.PROVIDER + "=? AND "
+ Imps.ContactList.ACCOUNT + "=?";
String[] selectionArgs = { list.getName(),
Long.toString(mProviderId),
Long.toString(mAccountId) };
Cursor cursor = mResolver.query(Imps.ContactList.CONTENT_URI,
CONTACT_LIST_ID_PROJECTION,
selection,
selectionArgs,
null); // no sort order
long listId = 0;
Uri uri = null;
try {
if (cursor.moveToFirst()) {
listId = cursor.getLong(0);
uri = ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, listId);
//Log.d(TAG,"Found and removing ContactList with name "+list.getName());
}
} finally {
cursor.close();
}
if (uri != null) {
// remove existing ContactList and Contacts of that list for replacement by the newly
// downloaded list
mResolver.delete(mContactUrl, Imps.Contacts.CONTACTLIST + "=?",
new String[]{Long.toString(listId)});
mResolver.delete(uri, selection, selectionArgs);
}
ContentValues contactListValues = new ContentValues(3);
contactListValues.put(Imps.ContactList.NAME, list.getName());
contactListValues.put(Imps.ContactList.PROVIDER, mProviderId);
contactListValues.put(Imps.ContactList.ACCOUNT, mAccountId);
//Log.d(TAG, "Adding ContactList name="+list.getName());
mValidatedContactLists.add(list.getName());
uri = mResolver.insert(Imps.ContactList.CONTENT_URI, contactListValues);
listId = ContentUris.parseId(uri);
synchronized (mContactLists) {
mContactLists.put(list.getAddress(),
new ContactListAdapter(list, listId));
}
Collection<Contact> contacts = list.getContacts();
if (contacts == null || contacts.size() == 0) {
return;
}
Iterator<Contact> iter = contacts.iterator();
while(iter.hasNext()) {
Contact c = iter.next();
String address = c.getAddress().getFullName();
if(isTemporary(address)) {
moveTemporaryContactToList(address, listId);
iter.remove();
}
mValidatedContacts.add(address);
}
ArrayList<String> usernames = new ArrayList<String>();
ArrayList<String> nicknames = new ArrayList<String>();
ArrayList<String> contactTypeArray = new ArrayList<String>();
for (Contact c : contacts) {
String username = c.getAddress().getFullName();
String nickname = c.getName();
int type = Imps.Contacts.TYPE_NORMAL;
if(isTemporary(username)) {
type = Imps.Contacts.TYPE_TEMPORARY;
}
if (isBlocked(username)) {
type = Imps.Contacts.TYPE_BLOCKED;
}
usernames.add(username);
nicknames.add(nickname);
contactTypeArray.add(String.valueOf(type));
}
ContentValues values = new ContentValues(6);
values.put(Imps.Contacts.PROVIDER, mProviderId);
values.put(Imps.Contacts.ACCOUNT, mAccountId);
values.put(Imps.Contacts.CONTACTLIST, listId);
values.putStringArrayList(Imps.Contacts.USERNAME, usernames);
values.putStringArrayList(Imps.Contacts.NICKNAME, nicknames);
values.putStringArrayList(Imps.Contacts.TYPE, contactTypeArray);
mResolver.insert(Imps.Contacts.BULK_CONTENT_URI, values);
}
void updateListNameInDataBase(ContactList list) {
ContactListAdapter listAdapter = getContactListAdapter(list.getAddress());
Uri uri = ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, listAdapter.getDataBaseId());
ContentValues values = new ContentValues(1);
values.put(Imps.ContactList.NAME, list.getName());
mResolver.update(uri, values, null, null);
}
void deleteContactFromDataBase(Contact contact, ContactList list) {
String selection = Imps.Contacts.USERNAME
+ "=? AND " + Imps.Contacts.CONTACTLIST + "=?";
long listId = getContactListAdapter(list.getAddress()).getDataBaseId();
String username = contact.getAddress().getFullName();
String[] selectionArgs = {username, Long.toString(listId)};
mResolver.delete(mContactUrl, selection, selectionArgs);
// clear the history message if the contact doesn't exist in any list
// anymore.
if(mAdaptee.getContact(contact.getAddress()) == null) {
clearHistoryMessages(username);
}
}
Uri insertContactContent(Contact contact, long listId) {
ContentValues values = getContactContentValues(contact, listId);
Uri uri = mResolver.insert(mContactUrl, values);
ContentValues presenceValues = getPresenceValues(ContentUris.parseId(uri),
contact.getPresence());
mResolver.insert(Imps.Presence.CONTENT_URI, presenceValues);
return uri;
}
private ContentValues getContactContentValues(Contact contact, long listId) {
final String username = contact.getAddress().getFullName();
final String nickname = contact.getName();
int type = Imps.Contacts.TYPE_NORMAL;
if(isTemporary(username)) {
type = Imps.Contacts.TYPE_TEMPORARY;
}
if (isBlocked(username)) {
type = Imps.Contacts.TYPE_BLOCKED;
}
ContentValues values = new ContentValues(4);
values.put(Imps.Contacts.USERNAME, username);
values.put(Imps.Contacts.NICKNAME, nickname);
values.put(Imps.Contacts.CONTACTLIST, listId);
values.put(Imps.Contacts.TYPE, type);
return values;
}
void clearHistoryMessages(String contact) {
Uri uri = Imps.Messages.getContentUriByContact(mAccountId, contact);
mResolver.delete(uri, null, null);
}
private ContentValues getPresenceValues(long contactId, Presence p) {
ContentValues values = new ContentValues(3);
values.put(Imps.Presence.CONTACT_ID, contactId);
values.put(Imps.Contacts.PRESENCE_STATUS, convertPresenceStatus(p));
values.put(Imps.Contacts.PRESENCE_CUSTOM_STATUS, p.getStatusText());
values.put(Imps.Presence.CLIENT_TYPE, translateClientType(p));
return values;
}
private int translateClientType(Presence presence) {
int clientType = presence.getClientType();
switch (clientType) {
case Presence.CLIENT_TYPE_MOBILE:
return Imps.Presence.CLIENT_TYPE_MOBILE;
default:
return Imps.Presence.CLIENT_TYPE_DEFAULT;
}
}
/**
* Converts the presence status to the value defined for ImProvider.
*
* @param presence The presence from the IM engine.
* @return The status value defined in for ImProvider.
*/
public static int convertPresenceStatus(Presence presence) {
switch (presence.getStatus()) {
case Presence.AVAILABLE:
return Imps.Presence.AVAILABLE;
case Presence.IDLE:
return Imps.Presence.IDLE;
case Presence.AWAY:
return Imps.Presence.AWAY;
case Presence.DO_NOT_DISTURB:
return Imps.Presence.DO_NOT_DISTURB;
case Presence.OFFLINE:
return Imps.Presence.OFFLINE;
}
// impossible...
Log.e(TAG, "Illegal presence status value " + presence.getStatus());
return Imps.Presence.AVAILABLE;
}
public void clearOnLogout() {
clearValidatedContactsAndLists();
clearTemporaryContacts();
clearPresence();
}
/**
* Clears the list of validated contacts and contact lists.
* As contacts and contacts lists are added after login, contacts and contact lists are
* stored as "validated contacts". After initial download of contacts is complete, any contacts
* and contact lists that remain in the database, but are not in the validated list, are
* obsolete and should be removed. This function resets that list for use upon login.
*/
private void clearValidatedContactsAndLists() {
// clear the list of validated contacts, contact lists, and blocked contacts
mValidatedContacts.clear();
mValidatedContactLists.clear();
mValidatedBlockedContacts.clear();
}
/**
* Clear the temporary contacts in the database. As contacts are persist between
* IM sessions, the temporary contacts need to be cleared after logout.
*/
private void clearTemporaryContacts() {
String selection = Imps.Contacts.CONTACTLIST + "=" + FAKE_TEMPORARY_LIST_ID;
mResolver.delete(mContactUrl, selection, null);
}
/**
* Clears the presence of the all contacts. As contacts are persist between
* IM sessions, the presence need to be cleared after logout.
*/
void clearPresence() {
StringBuilder where = new StringBuilder();
where.append(Imps.Presence.CONTACT_ID);
where.append(" in (select _id from contacts where ");
where.append(Imps.Contacts.ACCOUNT);
where.append("=");
where.append(mAccountId);
where.append(")");
mResolver.delete(Imps.Presence.CONTENT_URI, where.toString(), null);
}
void closeChatSession(String address) {
ChatSessionManagerAdapter sessionManager =
(ChatSessionManagerAdapter) mConn.getChatSessionManager();
ChatSessionAdapter session =
(ChatSessionAdapter) sessionManager.getChatSession(address);
if(session != null) {
session.leave();
}
}
void updateChatPresence(String address, String nickname, Presence p) {
ChatSessionManagerAdapter sessionManager =
(ChatSessionManagerAdapter) mConn.getChatSessionManager();
// TODO: This only find single chat sessions, we need to go through all
// active chat sessions and find if the contact is a participant of the
// session.
ChatSessionAdapter session =
(ChatSessionAdapter) sessionManager.getChatSession(address);
if(session != null) {
session.insertPresenceUpdatesMsg(nickname, p);
}
}
}