• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package com.android.im.engine;
19 
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Vector;
24 import java.util.concurrent.CopyOnWriteArrayList;
25 
26 /**
27  * ContactListManager manages the creating, removing and retrieving contact
28  * lists.
29  */
30 public abstract class ContactListManager {
31     /**
32      * ContactListManager state that indicates the contact list(s) has not been loaded.
33      */
34     public static final int LISTS_NOT_LOADED = 0;
35 
36     /**
37      * ContactListManager state that indicates the contact list(s) is loading.
38      */
39     public static final int LISTS_LOADING = 1;
40 
41     /**
42      * ContactListManager state that indicates the blocked list has been loaded.
43      */
44     public static final int BLOCKED_LIST_LOADED = 2;
45 
46     /**
47      * ContactListManager state that indicates the contact list(s) has been loaded.
48      */
49     public static final int LISTS_LOADED = 3;
50 
51     protected ContactList mDefaultContactList;
52     protected Vector<ContactList> mContactLists;
53 
54     protected CopyOnWriteArrayList<ContactListListener> mContactListListeners;
55     protected SubscriptionRequestListener mSubscriptionRequestListener;
56 
57     protected Vector<Contact> mBlockedList;
58 
59     private int mState;
60 
61     /**
62      * A pending list of blocking contacts which is used for checking duplicated
63      * block operation.
64      */
65     private Vector<String> mBlockPending;
66     /**
67      * A pending list of deleting contacts which is used for checking duplicated
68      * delete operation.
69      */
70     private Vector<Contact> mDeletePending;
71 
72     /**
73      * Creates a new ContactListManager.
74      *
75      * @param conn The underlying protocol connection.
76      */
ContactListManager()77     protected ContactListManager() {
78         mContactLists = new Vector<ContactList>();
79         mContactListListeners = new CopyOnWriteArrayList<ContactListListener>();
80         mBlockedList = new Vector<Contact>();
81 
82         mBlockPending = new Vector<String>(4);
83         mDeletePending = new Vector<Contact>(4);
84 
85         mState = LISTS_NOT_LOADED;
86     }
87 
88     /**
89      * Set the state of the ContactListManager
90      *
91      * @param state the new state
92      */
setState(int state)93     protected synchronized void setState(int state) {
94         if (state < LISTS_NOT_LOADED || state > LISTS_LOADED) {
95             throw new IllegalArgumentException();
96         }
97 
98         mState = state;
99     }
100 
101     /**
102      * Get the state of the ContactListManager
103      *
104      * @return the current state of the manager
105      */
getState()106     public synchronized int getState() {
107         return mState;
108     }
109 
110     /**
111      * Adds a listener to the manager so that it will be notified for contact
112      * list changed.
113      *
114      * @param listener the listener to add.
115      */
addContactListListener(ContactListListener listener)116     public synchronized void addContactListListener(ContactListListener listener) {
117         if ((listener != null) && !mContactListListeners.contains(listener)) {
118             mContactListListeners.add(listener);
119         }
120     }
121 
122     /**
123      * Removes a listener from this manager.
124      *
125      * @param listener the listener to remove.
126      */
removeContactListListener(ContactListListener listener)127     public synchronized void removeContactListListener(ContactListListener listener) {
128         mContactListListeners.remove(listener);
129     }
130 
131     /**
132      * Sets the SubscriptionRequestListener to the manager so that it will be notified
133      * when a subscription request from another user is received.
134      *
135      * @param listener the ContactInvitationListener.
136      */
setSubscriptionRequestListener( SubscriptionRequestListener listener)137     public synchronized void setSubscriptionRequestListener(
138             SubscriptionRequestListener listener) {
139         mSubscriptionRequestListener = listener;
140     }
141 
getSubscriptionRequestListener()142     public synchronized SubscriptionRequestListener getSubscriptionRequestListener() {
143         return mSubscriptionRequestListener;
144     }
145 
146     /**
147      * Gets a collection of the contact lists.
148      *
149      * @return a collection of the contact lists.
150      */
getContactLists()151     public Collection<ContactList> getContactLists() {
152         return Collections.unmodifiableCollection(mContactLists);
153     }
154 
155     /**
156      * Gets a contact by address.
157      *
158      * @param address the address of the Contact.
159      * @return the Contact or null if the Contact doesn't exist in any list.
160      */
getContact(Address address)161     public Contact getContact(Address address) {
162         return getContact(address.getFullName());
163     }
164 
getContact(String address)165     public Contact getContact(String address) {
166         for (ContactList list : mContactLists) {
167             Contact c = list.getContact(address);
168             if( c != null) {
169                 return c;
170             }
171         }
172         return null;
173     }
174 
normalizeAddress(String address)175     public abstract String normalizeAddress(String address);
176 
177     /**
178      * Creates a temporary contact. It's usually used when we want to create
179      * a chat with someone not in the list.
180      *
181      * @param address the address of the temporary contact.
182      * @return the created temporary contact
183      */
createTemporaryContact(String address)184     public abstract Contact createTemporaryContact(String address);
185 
186     /**
187      * Tell whether the manager contains the specified contact
188      *
189      * @param contact the specified contact
190      * @return true if the contact is contained in the lists of the manager,
191      *          otherwise, return false
192      */
containsContact(Contact contact)193     public boolean containsContact(Contact contact) {
194         for (ContactList list : mContactLists) {
195             if (list.containsContact(contact)) {
196                 return true;
197             }
198         }
199 
200         return false;
201     }
202 
203     /**
204      * Gets a contact list by name.
205      *
206      * @param name the name of the contact list.
207      * @return the ContactList or null if the contact list doesn't exist.
208      */
getContactList(String name)209     public ContactList getContactList(String name) {
210         for (ContactList list : mContactLists) {
211             if (list.getName() != null && list.getName().equals(name)) {
212                 return list;
213             }
214         }
215         return null;
216     }
217 
218     /**
219      * Get the contact list by the address
220      *
221      * @param address the address of the contact list
222      * @return the <code>ContactList</code> or null if the list doesn't exist
223      */
getContactList(Address address)224     public ContactList getContactList(Address address) {
225         for (ContactList list : mContactLists) {
226             if (list.getAddress().equals(address)) {
227                 return list;
228             }
229         }
230 
231         return null;
232     }
233 
234     /**
235      * Gets the default contact list.
236      *
237      * @return the default contact list.
238      * @throws ImException
239      */
getDefaultContactList()240     public ContactList getDefaultContactList() throws ImException {
241         checkState();
242         return mDefaultContactList;
243     }
244 
245     /**
246      * Create a contact list with the specified name asynchronously.
247      *
248      * @param name the specific name of the contact list
249      * @throws ImException
250      */
createContactListAsync(String name)251     public void createContactListAsync(String name) throws ImException {
252         createContactListAsync(name, null, false);
253     }
254 
255     /**
256      * Create a contact list with specified name and whether it is to be
257      * created as the default list.
258      *
259      * @param name the specific name of the contact list
260      * @param isDefault whether the contact list is to be created as the
261      *                  default list
262      * @throws ImException
263      */
createContactListAsync(String name, boolean isDefault)264     public void createContactListAsync(String name, boolean isDefault) throws ImException {
265         createContactListAsync(name, null, isDefault);
266     }
267 
268     /**
269      * Create a contact list with specified name and contacts asynchronously.
270      *
271      * @param name the specific name of the contact list
272      * @param contacts the initial contacts of the contact list
273      * @throws ImException
274      */
createContactListAsync(String name, Collection<Contact> contacts)275     public void createContactListAsync(String name,
276             Collection<Contact> contacts) throws ImException {
277         createContactListAsync(name, contacts, false);
278     }
279 
280     /**
281      * Create a contact list with specified name and contacts asynchronously,
282      * and whether it is to be created as the default contact list.
283      *
284      * @param name the name of the contact list
285      * @param contacts the initial contacts of the list
286      * @param isDefault whether the contact list is the default list
287      * @throws ImException
288      */
createContactListAsync(String name, Collection<Contact> contacts, boolean isDefault)289     public synchronized void createContactListAsync(String name,
290             Collection<Contact> contacts, boolean isDefault) throws ImException {
291         checkState();
292 
293         if (getContactList(name) != null) {
294             throw new ImException(ImErrorInfo.CONTACT_LIST_EXISTS,
295                     "Contact list already exists");
296         }
297 
298         if (mContactLists.isEmpty()) {
299             isDefault = true;
300         }
301 
302         doCreateContactListAsync(name, contacts, isDefault);
303     }
304 
305     /**
306      * Delete a contact list of the specified name asynchronously
307      * @param name the specific name of the contact list
308      * @throws ImException
309      */
deleteContactListAsync(String name)310     public void deleteContactListAsync(String name) throws ImException {
311         deleteContactListAsync(getContactList(name));
312     }
313 
314     /**
315      * Delete a specified contact list asynchronously
316      * @param list the contact list to be deleted
317      * @throws ImException if any error raised
318      */
deleteContactListAsync(ContactList list)319     public synchronized void deleteContactListAsync(ContactList list) throws ImException {
320         checkState();
321 
322         if (null == list) {
323             throw new ImException(ImErrorInfo.CONTACT_LIST_NOT_FOUND,
324                     "Contact list doesn't exist");
325         }
326 
327         doDeleteContactListAsync(list);
328     }
329 
blockContactAsync(Contact contact)330     public void blockContactAsync(Contact contact) throws ImException {
331         blockContactAsync(contact.getAddress().getFullName());
332     }
333 
334     /**
335      * Blocks a certain Contact. The contact will be removed from any
336      * ContactList after be blocked. If the contact has already been blocked,
337      * the method does nothing.
338      *
339      * @param address the address of the contact to block.
340      * @throws ImException if an error occurs
341      */
blockContactAsync(String address)342     public void blockContactAsync(String address) throws ImException {
343         checkState();
344 
345         if(isBlocked(address)){
346             return;
347         }
348 
349         if (mBlockPending.contains(address)) {
350             return;
351         }
352         doBlockContactAsync(address, true);
353     }
354 
unblockContactAsync(Contact contact)355     public void unblockContactAsync(Contact contact) throws ImException {
356         unblockContactAsync(contact.getAddress().getFullName());
357     }
358 
359     /**
360      * Unblock a certain contact. It will removes the contact from the blocked
361      * list and allows the contact to send message or invitation to the client
362      * again. If the contact is not blocked on the client, this method does
363      * nothing. Whether the unblocked contact will be added to the ContactList
364      * it belongs before blocked or not depends on the underlying protocol
365      * implementation.
366      *
367      * @param address the address of the contact to unblock.
368      * @throws ImException if the current state is illegal
369      */
unblockContactAsync(String address)370     public void unblockContactAsync(String address) throws ImException {
371         checkState();
372 
373         if(!isBlocked(address)) {
374             return;
375         }
376 
377         doBlockContactAsync(address, false);
378     }
379 
addContactToListAsync(String address, ContactList list)380     protected void addContactToListAsync(String address, ContactList list)
381             throws ImException {
382         checkState();
383 
384         doAddContactToListAsync(address, list);
385     }
386 
removeContactFromListAsync(Contact contact, ContactList list)387     protected void removeContactFromListAsync(Contact contact, ContactList list)
388             throws ImException {
389         checkState();
390 
391         if (mDeletePending.contains(contact)) {
392             return;
393         }
394 
395         doRemoveContactFromListAsync(contact, list);
396     }
397 
398     /**
399      * Gets a unmodifiable list of blocked contacts.
400      *
401      * @return a unmodifiable list of blocked contacts.
402      * @throws ImException
403      */
getBlockedList()404     public List<Contact> getBlockedList() throws ImException {
405         checkState();
406 
407         return Collections.unmodifiableList(mBlockedList);
408     }
409 
410     /**
411      * Checks if a contact is blocked.
412      *
413      * @param contact the contact.
414      * @return true if it's blocked, false otherwise.
415      * @throws ImException if contacts has not been loaded.
416      */
isBlocked(Contact contact)417     public boolean isBlocked(Contact contact) throws ImException {
418         return isBlocked(contact.getAddress().getFullName());
419     }
420 
421     /**
422      * Checks if a contact is blocked.
423      *
424      * @param address the address of the contact.
425      * @return true if it's blocked, false otherwise.
426      * @throws ImException if contacts has not been loaded.
427      */
isBlocked(String address)428     public synchronized boolean isBlocked(String address) throws ImException {
429         if(mState < BLOCKED_LIST_LOADED) {
430             throw new ImException(ImErrorInfo.ILLEGAL_CONTACT_LIST_MANAGER_STATE,
431                 "Blocked list hasn't been loaded");
432         }
433         for(Contact c : mBlockedList) {
434             if(c.getAddress().getFullName().equals(address)){
435                 return true;
436             }
437         }
438         return false;
439     }
440 
441     /**
442      * Check the state of the ContactListManager. Only the LIST_LOADED state
443      * is permitted.
444      *
445      * @throws ImException if the current state is not LIST_LOADED
446      */
checkState()447     protected void checkState() throws ImException {
448         if (getConnection().getState() != ImConnection.LOGGED_IN) {
449             throw new ImException(ImErrorInfo.CANT_CONNECT_TO_SERVER,
450                     "Can't connect to server");
451         }
452 
453         if (getState() != LISTS_LOADED) {
454             throw new ImException(ImErrorInfo.ILLEGAL_CONTACT_LIST_MANAGER_STATE,
455                     "Illegal contact list manager state");
456         }
457     }
458 
459     /**
460      * Load the contact lists from the server. This method will normally called
461      * after the user logged in to get the initial/saved contact lists from
462      * server. After called once, this method should not be called again.
463      */
loadContactListsAsync()464     public abstract void loadContactListsAsync();
465 
approveSubscriptionRequest(String contact)466     public abstract void approveSubscriptionRequest(String contact);
declineSubscriptionRequest(String contact)467     public abstract void declineSubscriptionRequest(String contact);
468 
getConnection()469     protected abstract ImConnection getConnection();
470 
471     /**
472      * Block or unblock a contact.
473      *
474      * @param address
475      *            the address of the contact to block or unblock.
476      * @param block
477      *            <code>true</code> to block the contact; <code>false</code>
478      *            to unblock the contact.
479      */
doBlockContactAsync(String address, boolean block)480     protected abstract void doBlockContactAsync(String address, boolean block);
doCreateContactListAsync(String name, Collection<Contact> contacts, boolean isDefault)481     protected abstract void doCreateContactListAsync(String name,
482             Collection<Contact> contacts, boolean isDefault);
doDeleteContactListAsync(ContactList list)483     protected abstract void doDeleteContactListAsync(ContactList list);
484 
485     /**
486      * Notify that the presence of the contact has been updated
487      *
488      * @param contacts the contacts who have updated presence information
489      */
notifyContactsPresenceUpdated(Contact[] contacts)490     protected void notifyContactsPresenceUpdated(Contact[] contacts) {
491         for (ContactListListener listener : mContactListListeners) {
492             listener.onContactsPresenceUpdate(contacts);
493         }
494     }
495 
496     /**
497      * Notify that a contact list related error has been raised.
498      *
499      * @param type the type of the error
500      * @param error the raised error
501      * @param listName the list name, if any, associated with the error
502      * @param contact the contact, if any, associated with the error
503      */
notifyContactError(int type, ImErrorInfo error, String listName, Contact contact)504     protected void notifyContactError(int type, ImErrorInfo error,
505             String listName, Contact contact) {
506         if (type == ContactListListener.ERROR_REMOVING_CONTACT) {
507             mDeletePending.remove(contact);
508         } else if (type == ContactListListener.ERROR_BLOCKING_CONTACT) {
509             mBlockPending.remove(contact.getAddress().getFullName());
510         }
511         for (ContactListListener listener : mContactListListeners) {
512             listener.onContactError(type, error, listName, contact);
513         }
514     }
515 
516     /**
517      * Notify that a contact list has been loaded
518      *
519      * @param list the loaded list
520      */
notifyContactListLoaded(ContactList list)521     protected void notifyContactListLoaded(ContactList list) {
522         for (ContactListListener listener : mContactListListeners) {
523             listener.onContactChange(ContactListListener.LIST_LOADED,
524                     list, null);
525         }
526     }
527 
528     /**
529      * Notify that all contact lists has been loaded
530      */
notifyContactListsLoaded()531     protected void notifyContactListsLoaded() {
532         setState(LISTS_LOADED);
533         for (ContactListListener listener : mContactListListeners) {
534             listener.onAllContactListsLoaded();
535         }
536     }
537 
538     /**
539      * Notify that a contact has been added to or removed from a list.
540      *
541      * @param list the updated contact list
542      * @param type the type of the update
543      * @param contact the involved contact, null if no contact involved.
544      */
notifyContactListUpdated(ContactList list, int type, Contact contact)545     protected void notifyContactListUpdated(ContactList list, int type,
546             Contact contact) {
547         synchronized (this) {
548             if (type == ContactListListener.LIST_CONTACT_ADDED) {
549                 list.insertToCache(contact);
550             } else if (type == ContactListListener.LIST_CONTACT_REMOVED) {
551                 list.removeFromCache(contact);
552                 mDeletePending.remove(contact);
553             }
554         }
555 
556         for (ContactListListener listener : mContactListListeners) {
557             listener.onContactChange(type, list, contact);
558         }
559     }
560 
561     /**
562      * Notify that the name of the specified contact list has been updated.
563      *
564      * @param list
565      * @param name the new name of the list
566      */
notifyContactListNameUpdated(ContactList list, String name)567     protected void notifyContactListNameUpdated(ContactList list, String name) {
568         list.mName = name;
569 
570         for (ContactListListener listener : mContactListListeners) {
571             listener.onContactChange(ContactListListener.LIST_RENAMED,
572                     list, null);
573         }
574     }
575 
576     /**
577      * Notify that a contact list has been created.
578      *
579      * @param list the created list
580      */
notifyContactListCreated(ContactList list)581     protected void notifyContactListCreated(ContactList list) {
582         synchronized (this) {
583             if (list.isDefault()) {
584                 for (ContactList l : mContactLists) {
585                     l.setDefault(false);
586                 }
587                 mDefaultContactList = list;
588             }
589             mContactLists.add(list);
590         }
591 
592         for (ContactListListener listener : mContactListListeners) {
593             listener.onContactChange(ContactListListener.LIST_CREATED,
594                     list, null);
595         }
596     }
597 
598     /**
599      * Notify that a contact list has been deleted
600      *
601      * @param list the deleted list
602      */
notifyContactListDeleted(ContactList list)603     protected void notifyContactListDeleted(ContactList list) {
604         synchronized(this) {
605             mContactLists.remove(list);
606             if (list.isDefault() && mContactLists.size() > 0) {
607                 mContactLists.get(0).setDefault(true);
608             }
609         }
610 
611         for (ContactListListener listener : mContactListListeners) {
612             listener.onContactChange(ContactListListener.LIST_DELETED,
613                     list, null);
614         }
615     }
616 
617     /**
618      * Notify that a contact has been blocked/unblocked.
619      *
620      * @param contact the blocked/unblocked contact
621      */
notifyBlockContact(Contact contact, boolean blocked)622     protected void notifyBlockContact(Contact contact, boolean blocked) {
623         synchronized (this) {
624             if (blocked) {
625                 mBlockedList.add(contact);
626                 String addr = contact.getAddress().getFullName();
627                 mBlockPending.remove(addr);
628             } else {
629                 mBlockedList.remove(contact);
630             }
631         }
632         for (ContactListListener listener : mContactListListeners) {
633             listener.onContactChange(blocked ? ContactListListener.CONTACT_BLOCKED
634                     : ContactListListener.CONTACT_UNBLOCKED, null, contact);
635         }
636     }
637 
doAddContactToListAsync(String address, ContactList list)638     protected abstract void doAddContactToListAsync(String address,
639             ContactList list) throws ImException;
doRemoveContactFromListAsync(Contact contact, ContactList list)640     protected abstract void doRemoveContactFromListAsync(Contact contact, ContactList list);
setListNameAsync(String name, ContactList list)641     protected abstract void setListNameAsync(String name, ContactList list);
642 }
643