FileDocCategorySizeDatePackage
ContactListManager.javaAPI DocAndroid 1.5 API20624Wed May 06 22:42:46 BST 2009com.android.im.engine

ContactListManager.java

/*
 * 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.engine;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * ContactListManager manages the creating, removing and retrieving contact
 * lists.
 */
public abstract class ContactListManager {
    /**
     * ContactListManager state that indicates the contact list(s) has not been loaded.
     */
    public static final int LISTS_NOT_LOADED = 0;

    /**
     * ContactListManager state that indicates the contact list(s) is loading.
     */
    public static final int LISTS_LOADING = 1;

    /**
     * ContactListManager state that indicates the blocked list has been loaded.
     */
    public static final int BLOCKED_LIST_LOADED = 2;

    /**
     * ContactListManager state that indicates the contact list(s) has been loaded.
     */
    public static final int LISTS_LOADED = 3;

    protected ContactList mDefaultContactList;
    protected Vector<ContactList> mContactLists;

    protected CopyOnWriteArrayList<ContactListListener> mContactListListeners;
    protected SubscriptionRequestListener mSubscriptionRequestListener;

    protected Vector<Contact> mBlockedList;

    private int mState;

    /**
     * A pending list of blocking contacts which is used for checking duplicated
     * block operation.
     */
    private Vector<String> mBlockPending;
    /**
     * A pending list of deleting contacts which is used for checking duplicated
     * delete operation.
     */
    private Vector<Contact> mDeletePending;

    /**
     * Creates a new ContactListManager.
     *
     * @param conn The underlying protocol connection.
     */
    protected ContactListManager() {
        mContactLists = new Vector<ContactList>();
        mContactListListeners = new CopyOnWriteArrayList<ContactListListener>();
        mBlockedList = new Vector<Contact>();

        mBlockPending = new Vector<String>(4);
        mDeletePending = new Vector<Contact>(4);

        mState = LISTS_NOT_LOADED;
    }

    /**
     * Set the state of the ContactListManager
     *
     * @param state the new state
     */
    protected synchronized void setState(int state) {
        if (state < LISTS_NOT_LOADED || state > LISTS_LOADED) {
            throw new IllegalArgumentException();
        }

        mState = state;
    }

    /**
     * Get the state of the ContactListManager
     *
     * @return the current state of the manager
     */
    public synchronized int getState() {
        return mState;
    }

    /**
     * Adds a listener to the manager so that it will be notified for contact
     * list changed.
     *
     * @param listener the listener to add.
     */
    public synchronized void addContactListListener(ContactListListener listener) {
        if ((listener != null) && !mContactListListeners.contains(listener)) {
            mContactListListeners.add(listener);
        }
    }

    /**
     * Removes a listener from this manager.
     *
     * @param listener the listener to remove.
     */
    public synchronized void removeContactListListener(ContactListListener listener) {
        mContactListListeners.remove(listener);
    }

    /**
     * Sets the SubscriptionRequestListener to the manager so that it will be notified
     * when a subscription request from another user is received.
     *
     * @param listener the ContactInvitationListener.
     */
    public synchronized void setSubscriptionRequestListener(
            SubscriptionRequestListener listener) {
        mSubscriptionRequestListener = listener;
    }

    public synchronized SubscriptionRequestListener getSubscriptionRequestListener() {
        return mSubscriptionRequestListener;
    }

    /**
     * Gets a collection of the contact lists.
     *
     * @return a collection of the contact lists.
     */
    public Collection<ContactList> getContactLists() {
        return Collections.unmodifiableCollection(mContactLists);
    }

    /**
     * Gets a contact by address.
     *
     * @param address the address of the Contact.
     * @return the Contact or null if the Contact doesn't exist in any list.
     */
    public Contact getContact(Address address) {
        return getContact(address.getFullName());
    }

    public Contact getContact(String address) {
        for (ContactList list : mContactLists) {
            Contact c = list.getContact(address);
            if( c != null) {
                return c;
            }
        }
        return null;
    }

    public abstract String normalizeAddress(String address);

    /**
     * Creates a temporary contact. It's usually used when we want to create
     * a chat with someone not in the list.
     *
     * @param address the address of the temporary contact.
     * @return the created temporary contact
     */
    public abstract Contact createTemporaryContact(String address);

    /**
     * Tell whether the manager contains the specified contact
     *
     * @param contact the specified contact
     * @return true if the contact is contained in the lists of the manager,
     *          otherwise, return false
     */
    public boolean containsContact(Contact contact) {
        for (ContactList list : mContactLists) {
            if (list.containsContact(contact)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Gets a contact list by name.
     *
     * @param name the name of the contact list.
     * @return the ContactList or null if the contact list doesn't exist.
     */
    public ContactList getContactList(String name) {
        for (ContactList list : mContactLists) {
            if (list.getName() != null && list.getName().equals(name)) {
                return list;
            }
        }
        return null;
    }

    /**
     * Get the contact list by the address
     *
     * @param address the address of the contact list
     * @return the <code>ContactList</code> or null if the list doesn't exist
     */
    public ContactList getContactList(Address address) {
        for (ContactList list : mContactLists) {
            if (list.getAddress().equals(address)) {
                return list;
            }
        }

        return null;
    }

    /**
     * Gets the default contact list.
     *
     * @return the default contact list.
     * @throws ImException
     */
    public ContactList getDefaultContactList() throws ImException {
        checkState();
        return mDefaultContactList;
    }

    /**
     * Create a contact list with the specified name asynchronously.
     *
     * @param name the specific name of the contact list
     * @throws ImException
     */
    public void createContactListAsync(String name) throws ImException {
        createContactListAsync(name, null, false);
    }

    /**
     * Create a contact list with specified name and whether it is to be
     * created as the default list.
     *
     * @param name the specific name of the contact list
     * @param isDefault whether the contact list is to be created as the
     *                  default list
     * @throws ImException
     */
    public void createContactListAsync(String name, boolean isDefault) throws ImException {
        createContactListAsync(name, null, isDefault);
    }

    /**
     * Create a contact list with specified name and contacts asynchronously.
     *
     * @param name the specific name of the contact list
     * @param contacts the initial contacts of the contact list
     * @throws ImException
     */
    public void createContactListAsync(String name,
            Collection<Contact> contacts) throws ImException {
        createContactListAsync(name, contacts, false);
    }

    /**
     * Create a contact list with specified name and contacts asynchronously,
     * and whether it is to be created as the default contact list.
     *
     * @param name the name of the contact list
     * @param contacts the initial contacts of the list
     * @param isDefault whether the contact list is the default list
     * @throws ImException
     */
    public synchronized void createContactListAsync(String name,
            Collection<Contact> contacts, boolean isDefault) throws ImException {
        checkState();

        if (getContactList(name) != null) {
            throw new ImException(ImErrorInfo.CONTACT_LIST_EXISTS,
                    "Contact list already exists");
        }

        if (mContactLists.isEmpty()) {
            isDefault = true;
        }

        doCreateContactListAsync(name, contacts, isDefault);
    }

    /**
     * Delete a contact list of the specified name asynchronously
     * @param name the specific name of the contact list
     * @throws ImException
     */
    public void deleteContactListAsync(String name) throws ImException {
        deleteContactListAsync(getContactList(name));
    }

    /**
     * Delete a specified contact list asynchronously
     * @param list the contact list to be deleted
     * @throws ImException if any error raised
     */
    public synchronized void deleteContactListAsync(ContactList list) throws ImException {
        checkState();

        if (null == list) {
            throw new ImException(ImErrorInfo.CONTACT_LIST_NOT_FOUND,
                    "Contact list doesn't exist");
        }

        doDeleteContactListAsync(list);
    }

    public void blockContactAsync(Contact contact) throws ImException {
        blockContactAsync(contact.getAddress().getFullName());
    }

    /**
     * Blocks a certain Contact. The contact will be removed from any
     * ContactList after be blocked. If the contact has already been blocked,
     * the method does nothing.
     *
     * @param address the address of the contact to block.
     * @throws ImException if an error occurs
     */
    public void blockContactAsync(String address) throws ImException {
        checkState();

        if(isBlocked(address)){
            return;
        }

        if (mBlockPending.contains(address)) {
            return;
        }
        doBlockContactAsync(address, true);
    }

    public void unblockContactAsync(Contact contact) throws ImException {
        unblockContactAsync(contact.getAddress().getFullName());
    }

    /**
     * Unblock a certain contact. It will removes the contact from the blocked
     * list and allows the contact to send message or invitation to the client
     * again. If the contact is not blocked on the client, this method does
     * nothing. Whether the unblocked contact will be added to the ContactList
     * it belongs before blocked or not depends on the underlying protocol
     * implementation.
     *
     * @param address the address of the contact to unblock.
     * @throws ImException if the current state is illegal
     */
    public void unblockContactAsync(String address) throws ImException {
        checkState();

        if(!isBlocked(address)) {
            return;
        }

        doBlockContactAsync(address, false);
    }

    protected void addContactToListAsync(String address, ContactList list)
            throws ImException {
        checkState();

        doAddContactToListAsync(address, list);
    }

    protected void removeContactFromListAsync(Contact contact, ContactList list)
            throws ImException {
        checkState();

        if (mDeletePending.contains(contact)) {
            return;
        }

        doRemoveContactFromListAsync(contact, list);
    }

    /**
     * Gets a unmodifiable list of blocked contacts.
     *
     * @return a unmodifiable list of blocked contacts.
     * @throws ImException
     */
    public List<Contact> getBlockedList() throws ImException {
        checkState();

        return Collections.unmodifiableList(mBlockedList);
    }

    /**
     * Checks if a contact is blocked.
     *
     * @param contact the contact.
     * @return true if it's blocked, false otherwise.
     * @throws ImException if contacts has not been loaded.
     */
    public boolean isBlocked(Contact contact) throws ImException {
        return isBlocked(contact.getAddress().getFullName());
    }

    /**
     * Checks if a contact is blocked.
     *
     * @param address the address of the contact.
     * @return true if it's blocked, false otherwise.
     * @throws ImException if contacts has not been loaded.
     */
    public synchronized boolean isBlocked(String address) throws ImException {
        if(mState < BLOCKED_LIST_LOADED) {
            throw new ImException(ImErrorInfo.ILLEGAL_CONTACT_LIST_MANAGER_STATE,
                "Blocked list hasn't been loaded");
        }
        for(Contact c : mBlockedList) {
            if(c.getAddress().getFullName().equals(address)){
                return true;
            }
        }
        return false;
    }

    /**
     * Check the state of the ContactListManager. Only the LIST_LOADED state
     * is permitted.
     *
     * @throws ImException if the current state is not LIST_LOADED
     */
    protected void checkState() throws ImException {
        if (getConnection().getState() != ImConnection.LOGGED_IN) {
            throw new ImException(ImErrorInfo.CANT_CONNECT_TO_SERVER,
                    "Can't connect to server");
        }

        if (getState() != LISTS_LOADED) {
            throw new ImException(ImErrorInfo.ILLEGAL_CONTACT_LIST_MANAGER_STATE,
                    "Illegal contact list manager state");
        }
    }

    /**
     * Load the contact lists from the server. This method will normally called
     * after the user logged in to get the initial/saved contact lists from
     * server. After called once, this method should not be called again.
     */
    public abstract void loadContactListsAsync();

    public abstract void approveSubscriptionRequest(String contact);
    public abstract void declineSubscriptionRequest(String contact);

    protected abstract ImConnection getConnection();

    /**
     * Block or unblock a contact.
     *
     * @param address
     *            the address of the contact to block or unblock.
     * @param block
     *            <code>true</code> to block the contact; <code>false</code>
     *            to unblock the contact.
     */
    protected abstract void doBlockContactAsync(String address, boolean block);
    protected abstract void doCreateContactListAsync(String name,
            Collection<Contact> contacts, boolean isDefault);
    protected abstract void doDeleteContactListAsync(ContactList list);

    /**
     * Notify that the presence of the contact has been updated
     *
     * @param contacts the contacts who have updated presence information
     */
    protected void notifyContactsPresenceUpdated(Contact[] contacts) {
        for (ContactListListener listener : mContactListListeners) {
            listener.onContactsPresenceUpdate(contacts);
        }
    }

    /**
     * Notify that a contact list related error has been raised.
     *
     * @param type the type of the error
     * @param error the raised error
     * @param listName the list name, if any, associated with the error
     * @param contact the contact, if any, associated with the error
     */
    protected void notifyContactError(int type, ImErrorInfo error,
            String listName, Contact contact) {
        if (type == ContactListListener.ERROR_REMOVING_CONTACT) {
            mDeletePending.remove(contact);
        } else if (type == ContactListListener.ERROR_BLOCKING_CONTACT) {
            mBlockPending.remove(contact.getAddress().getFullName());
        }
        for (ContactListListener listener : mContactListListeners) {
            listener.onContactError(type, error, listName, contact);
        }
    }

    /**
     * Notify that a contact list has been loaded
     *
     * @param list the loaded list
     */
    protected void notifyContactListLoaded(ContactList list) {
        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(ContactListListener.LIST_LOADED,
                    list, null);
        }
    }

    /**
     * Notify that all contact lists has been loaded
     */
    protected void notifyContactListsLoaded() {
        setState(LISTS_LOADED);
        for (ContactListListener listener : mContactListListeners) {
            listener.onAllContactListsLoaded();
        }
    }

    /**
     * Notify that a contact has been added to or removed from a list.
     *
     * @param list the updated contact list
     * @param type the type of the update
     * @param contact the involved contact, null if no contact involved.
     */
    protected void notifyContactListUpdated(ContactList list, int type,
            Contact contact) {
        synchronized (this) {
            if (type == ContactListListener.LIST_CONTACT_ADDED) {
                list.insertToCache(contact);
            } else if (type == ContactListListener.LIST_CONTACT_REMOVED) {
                list.removeFromCache(contact);
                mDeletePending.remove(contact);
            }
        }

        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(type, list, contact);
        }
    }

    /**
     * Notify that the name of the specified contact list has been updated.
     *
     * @param list
     * @param name the new name of the list
     */
    protected void notifyContactListNameUpdated(ContactList list, String name) {
        list.mName = name;

        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(ContactListListener.LIST_RENAMED,
                    list, null);
        }
    }

    /**
     * Notify that a contact list has been created.
     *
     * @param list the created list
     */
    protected void notifyContactListCreated(ContactList list) {
        synchronized (this) {
            if (list.isDefault()) {
                for (ContactList l : mContactLists) {
                    l.setDefault(false);
                }
                mDefaultContactList = list;
            }
            mContactLists.add(list);
        }

        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(ContactListListener.LIST_CREATED,
                    list, null);
        }
    }

    /**
     * Notify that a contact list has been deleted
     *
     * @param list the deleted list
     */
    protected void notifyContactListDeleted(ContactList list) {
        synchronized(this) {
            mContactLists.remove(list);
            if (list.isDefault() && mContactLists.size() > 0) {
                mContactLists.get(0).setDefault(true);
            }
        }

        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(ContactListListener.LIST_DELETED,
                    list, null);
        }
    }

    /**
     * Notify that a contact has been blocked/unblocked.
     *
     * @param contact the blocked/unblocked contact
     */
    protected void notifyBlockContact(Contact contact, boolean blocked) {
        synchronized (this) {
            if (blocked) {
                mBlockedList.add(contact);
                String addr = contact.getAddress().getFullName();
                mBlockPending.remove(addr);
            } else {
                mBlockedList.remove(contact);
            }
        }
        for (ContactListListener listener : mContactListListeners) {
            listener.onContactChange(blocked ? ContactListListener.CONTACT_BLOCKED
                    : ContactListListener.CONTACT_UNBLOCKED, null, contact);
        }
    }

    protected abstract void doAddContactToListAsync(String address,
            ContactList list) throws ImException;
    protected abstract void doRemoveContactFromListAsync(Contact contact, ContactList list);
    protected abstract void setListNameAsync(String name, ContactList list);
}