001    package com.skype.api;
002    
003    import com.skype.ipc.SidRoot;
004    import com.skype.ipc.SidObject;
005    import com.skype.ipc.EnumConverting;
006    import com.skype.ipc.PropertyEnumConverting;
007    import com.skype.ipc.Decoding;
008    import com.skype.ipc.Encoding;
009    import com.skype.ipc.Encoding;
010    import java.io.IOException;
011    import com.skype.ipc.PropertyEnumConverting;
012    import com.skype.ipc.SidGetResponding;
013    
014    /**
015     * Collects and manages Contacts related by type, status, or some other arbitrary criteria. SkypeKit recognizes two distinct ContactGroup flavors - "hardwired" and "custom". SkypeKit both defines the criteria for and dynamically manages all "hardwired" ContactGroups. Individual users explicitly create and manage all "custom" ContactGroups.  
016     * 
017     * "Hardwired" groups are primarily organizational tools, for example, they enable you to display a list of all Contacts awaiting authorization by you. "Custom" groups are also organizational tools, for example, they enable you to display a list of all Contacts in a particular geographical area or belonging to a particular professional association, social clubs, and so forth. Primarily, though, "custom" groups are functional tools that enable you to establish conference calls, group chats, and so forth. 
018     * 
019     * "Hardwired" ContactGroups are defined for and available to all users. SkypeKit determines membership in a particular "hardwired" group dynamically whenever a user invokes Skype.GetHardwiredContactGroup for that group. Subsequent changes to a Contact's status might result in its being added to (for example, the Contact is now authorized) or removed from (for example, the Contact is now removed or blocked) one or more "hardwired" groups. 
020     * 
021     * SkypeKit fires OnChange events for all affected ContractGroup instances. Essentially all ContactGroup methods related to explicitly adding and removing members and conversations from the group return false, and CanAdd and CanRemove additionally return a false result. 
022     * 
023     * "Custom" ContactGroups can be defined by a particular Skype user through the UI. Your UI should implement Creation, deletion and filtering contact list by custom contact groups, as well as adding and removing contacts in those groups. 
024     * 
025     * A Contact can belong to multiple non-mutually exclusive "hardwired" groups at the same time, for example, an authorized contact is typically in your "buddy" group, but a Contact cannot belong to CONTACTS_AUTHORIZED_BY_ME if they are awaiting authorization. Similarly, a Contact can belong to multiple "custom" groups and mutual exclusivity is typically not an issue. 
026     */
027    public final class ContactGroup extends SidObject {
028            /** The list of all possible ContactGroup types. A value of this type can be passed to Skype class GetHardwiredContactGroup to retrieve the relevant ContactGroup object.  */
029            public enum Type implements EnumConverting {
030                    /** The superset of all "hardwired" contact groups.  */
031                    ALL_KNOWN_CONTACTS               (1),
032                    /** The set of all authorized contacts, that is, contacts that were last the target of Contact.SetBuddyStatus(false) plus all SkypeOut contacts.  */
033                    ALL_BUDDIES                      (2),
034                    /**
035                     * The set of all authorized Skype contacts (Contact:_SKYPENAME is non-null).  
036                     * Note that this excludes Skype contacts that have either never been the target of Contact.SetBuddyStatus(true) or were last the target of Contactact.SetBuddyStatus(false). 
037                     */
038                    SKYPE_BUDDIES                    (3),
039                    /** The set of all SkypeOut contacts (Contact:_PSTNNUMBER is non-null). PSTN contacts can be added to the contact list by retrieving a new contact object with Skype.GetContact, passing in the phone number as string, and then either using Contact.SetBuddyStatus(true) or adding the contact to the SKYPEOUT_BUDDIES group with ContactGroup.AddContact.  */
040                    SKYPEOUT_BUDDIES                 (4),
041                    /** The subset of ALL_BUDDIES that are currently online, including those currently marked as DO_NOT_DISTURBED and AWAY.  */
042                    ONLINE_BUDDIES                   (5),
043                    /** The set of all contacts whose Contact:_TYPE reflects UNRECOGNIZED OR have not authorized the local user yet.  */
044                    UNKNOWN_OR_PENDING_AUTH_BUDDIES  (6),
045                    /** This filter returns top 10 most recently contacted contacts, based on Contact.P_LASTUSED_TIMESTAMP property values. This is not configurable. Note that the P_LASTUSED_TIMESTAMP property does not propagate between different Skype instances - thus this filter only works in context of the local database. Recent contacts that were in touch with the user on some other Skype installation will not show up in this filter.  */
046                    RECENTLY_CONTACTED_CONTACTS      (7),
047                    /** Contacts to whose authorization request the user has not responded yet. The UI should enable the user to accept, decline the authorization request and in case of decline, optionally block further incoming communication from the contact. See: Contact.SetBuddyStatus, Contact.SetBlocked and Contact.IgnoreAuthRequest for more information.  */
048                    CONTACTS_WAITING_MY_AUTHORIZATION(8),
049                    /** All contacts authorized by the user.  */
050                    CONTACTS_AUTHORIZED_BY_ME        (9),
051                    /** Group of contacts the user has blocked from further incoming communications. If the UI enables contact blocking, it should also provide interface for the user to unblock the blocked contacts. Note that a contact can simultaneously be in both CONTACTS_BLOCKED_BY_ME and CONTACTS_AUTHORIZED_BY_ME groups.  */
052                    CONTACTS_BLOCKED_BY_ME           (10),
053                    /** The set of all "buddies" that are not also a member of a custom group.  */
054                    UNGROUPED_BUDDIES                (11),
055                    /** A custom group defined by user.  */
056                    CUSTOM_GROUP                     (12),
057                    /** The shared contact group functionality is no longer supported. This contact group type can be ignored.  */
058                    PROPOSED_SHARED_GROUP            (13),
059                    /** The shared contact group functionality is no longer supported. This contact group type can be ignored.  */
060                    SHARED_GROUP                     (14),
061                    /** The set of all contacts that were originally imported from an external address book.  */
062                    EXTERNAL_CONTACTS                (15);
063                    private final int key;
064                    Type(int key) {
065                            this.key = key;
066                    };
067                    public int getId()   { return key; }
068                    public EnumConverting getDefault() { return ALL_KNOWN_CONTACTS; }
069                    public EnumConverting convert(int from) { return Type.get(from); }
070                    public EnumConverting[] getArray(final int size) { return new Type[size]; }
071                    public static Type get(int from) {
072                            switch (from) {
073                            case  1: return ALL_KNOWN_CONTACTS;
074                            case  2: return ALL_BUDDIES;
075                            case  3: return SKYPE_BUDDIES;
076                            case  4: return SKYPEOUT_BUDDIES;
077                            case  5: return ONLINE_BUDDIES;
078                            case  6: return UNKNOWN_OR_PENDING_AUTH_BUDDIES;
079                            case  7: return RECENTLY_CONTACTED_CONTACTS;
080                            case  8: return CONTACTS_WAITING_MY_AUTHORIZATION;
081                            case  9: return CONTACTS_AUTHORIZED_BY_ME;
082                            case 10: return CONTACTS_BLOCKED_BY_ME;
083                            case 11: return UNGROUPED_BUDDIES;
084                            case 12: return CUSTOM_GROUP;
085                            case 13: return PROPOSED_SHARED_GROUP;
086                            case 14: return SHARED_GROUP;
087                            case 15: return EXTERNAL_CONTACTS;
088                            }
089                            return ALL_KNOWN_CONTACTS;
090                    }
091                    public static final int ALL_KNOWN_CONTACTS_VALUE                =  1;
092                    public static final int ALL_BUDDIES_VALUE                       =  2;
093                    public static final int SKYPE_BUDDIES_VALUE                     =  3;
094                    public static final int SKYPEOUT_BUDDIES_VALUE                  =  4;
095                    public static final int ONLINE_BUDDIES_VALUE                    =  5;
096                    public static final int UNKNOWN_OR_PENDING_AUTH_BUDDIES_VALUE   =  6;
097                    public static final int RECENTLY_CONTACTED_CONTACTS_VALUE       =  7;
098                    public static final int CONTACTS_WAITING_MY_AUTHORIZATION_VALUE =  8;
099                    public static final int CONTACTS_AUTHORIZED_BY_ME_VALUE         =  9;
100                    public static final int CONTACTS_BLOCKED_BY_ME_VALUE            = 10;
101                    public static final int UNGROUPED_BUDDIES_VALUE                 = 11;
102                    public static final int CUSTOM_GROUP_VALUE                      = 12;
103                    public static final int PROPOSED_SHARED_GROUP_VALUE             = 13;
104                    public static final int SHARED_GROUP_VALUE                      = 14;
105                    public static final int EXTERNAL_CONTACTS_VALUE                 = 15;
106            }
107            private final static byte[] P_TYPE_req = {(byte) 90,(byte) 71,(byte) 155,(byte) 1,(byte) 93,(byte) 10};
108            private final static byte[] P_CUSTOM_GROUP_ID_req = {(byte) 90,(byte) 71,(byte) 154,(byte) 1,(byte) 93,(byte) 10};
109            private final static byte[] P_GIVEN_DISPLAY_NAME_req = {(byte) 90,(byte) 71,(byte) 151,(byte) 1,(byte) 93,(byte) 10};
110            private final static byte[] P_CONTACT_COUNT_req = {(byte) 90,(byte) 71,(byte) 152,(byte) 1,(byte) 93,(byte) 10};        private final static byte[] P_ONLINE_CONTACT_COUNT_req = {(byte) 90,(byte) 71,(byte) 153,(byte) 1,(byte) 93,(byte) 10};
111            /** Properties of the ContactGroup class */
112            public enum Property implements PropertyEnumConverting {
113                    P_UNKNOWN             (0,0,null,0,null),
114                    P_TYPE                (155, 1, P_TYPE_req, 0, Type.get(0)),
115                    P_CUSTOM_GROUP_ID     (154, 2, P_CUSTOM_GROUP_ID_req, 0, null),
116                    P_GIVEN_DISPLAY_NAME  (151, 3, P_GIVEN_DISPLAY_NAME_req, 0, null),
117                    P_CONTACT_COUNT       (152, 4, P_CONTACT_COUNT_req, 0, null),
118                    P_ONLINE_CONTACT_COUNT(153, 5, P_ONLINE_CONTACT_COUNT_req, 0, null);
119                    private final int    key;
120                    private final int    idx;
121                    private final byte[] req;
122                    private final int    mod;
123                    private final EnumConverting enumConverter;
124                    Property(int key, int idx, byte[] req, int mod, EnumConverting converter) {
125                            this.key = key;
126                            this.idx = idx;
127                            this.req = req;
128                            this.mod = mod;
129                            this.enumConverter = converter;
130                    };
131                    public boolean  isCached()    { return idx > 0;   }
132                    public int      getIdx()      { return idx;       }
133                    public int      getId()       { return key;       }
134                    public byte[]   getRequest()  { return req;       }
135                    public EnumConverting getDefault()  { return P_UNKNOWN; }
136                    public int      getModuleId() { return mod;       }
137                    public EnumConverting getEnumConverter()    { return enumConverter;   }
138                    public EnumConverting convert(final int from) { return Property.get(from); }
139                    public EnumConverting[] getArray(final int size) { return new Property[size]; }
140                    public static Property get(final int from) {
141                            switch (from) {
142                            case 155: return P_TYPE;
143                            case 154: return P_CUSTOM_GROUP_ID;
144                            case 151: return P_GIVEN_DISPLAY_NAME;
145                            case 152: return P_CONTACT_COUNT;
146                            case 153: return P_ONLINE_CONTACT_COUNT;
147                            }
148                            return P_UNKNOWN;
149                    }
150                    public static final int P_TYPE_VALUE                 = 155;
151                    public static final int P_CUSTOM_GROUP_ID_VALUE      = 154;
152                    public static final int P_GIVEN_DISPLAY_NAME_VALUE   = 151;
153                    public static final int P_CONTACT_COUNT_VALUE        = 152;
154                    public static final int P_ONLINE_CONTACT_COUNT_VALUE = 153;
155            }
156            private final static byte[] giveDisplayName_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 1};
157            /** Setter for ContactGroup class GIVEN_DISPLAYNAME property.  * @param name
158             */
159            public void giveDisplayName(String name) {
160                    try {
161                            sidDoRequest(giveDisplayName_req)
162                            .addStringParm(1, name)
163                            .endOneWay();
164                    } catch(IOException e) {
165                            mSidRoot.sidOnFatalError(e);
166                    }
167            }
168            private final static byte[] delete_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 2};
169            /** Removes the contact group. This is synced across instances logged in with the same account - which can take several minutes for the sync to happen.  * @return result
170             */
171            public boolean delete() {
172                    try {
173                            return sidDoRequest(delete_req)
174                            .endRequest().getBoolParm(1, true);
175                    } catch(IOException e) {
176                            mSidRoot.sidOnFatalError(e);
177                            return false
178                    ;}
179            }
180            private final static byte[] getConversations_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 3};
181            /** Returns list of conversations in the ContactGroup.  * @return conversations
182             */
183            public Conversation[] getConversations() {
184                    try {
185                            return (Conversation[]) sidDoRequest(getConversations_req)
186                            .endRequest().getObjectListParm(1, 18, true);
187                    } catch(IOException e) {
188                            mSidRoot.sidOnFatalError(e);
189                            return null
190                    ;}
191            }
192            private final static byte[] canAddConversation_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 4};
193            /** Checks if the current user can add given conversation to the ContactGroup. Returns false for most of the hardwired contact groups for example.  * @param conversation Conversation to be checked. 
194             * @return result Returns true if Conversation can be added to this ContactGroup. 
195             */
196            public boolean canAddConversation(Conversation conversation) {
197                    try {
198                            return sidDoRequest(canAddConversation_req)
199                            .addObjectParm(1, conversation)
200                            .endRequest().getBoolParm(1, true);
201                    } catch(IOException e) {
202                            mSidRoot.sidOnFatalError(e);
203                            return false
204                    ;}
205            }
206            private final static byte[] addConversation_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 5};
207            /** Adds given conversation to the ContactGroup.  * @param conversation
208             */
209            public void addConversation(Conversation conversation) {
210                    try {
211                            sidDoRequest(addConversation_req)
212                            .addObjectParm(1, conversation)
213                            .endOneWay();
214                    } catch(IOException e) {
215                            mSidRoot.sidOnFatalError(e);
216                    }
217            }
218            private final static byte[] canRemoveConversation_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 6};
219            /** Checks if the current user can remove given conversation from the ContactGroup. Again, returns false for most hardwired contact groups.  * @return result true if RemoveConversation(contact) works on this group
220             */
221            public boolean canRemoveConversation() {
222                    try {
223                            return sidDoRequest(canRemoveConversation_req)
224                            .endRequest().getBoolParm(1, true);
225                    } catch(IOException e) {
226                            mSidRoot.sidOnFatalError(e);
227                            return false
228                    ;}
229            }
230            private final static byte[] removeConversation_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 7};
231            /** Removes given conversation from the ContactGroup.  * @param conversation
232             */
233            public void removeConversation(Conversation conversation) {
234                    try {
235                            sidDoRequest(removeConversation_req)
236                            .addObjectParm(1, conversation)
237                            .endOneWay();
238                    } catch(IOException e) {
239                            mSidRoot.sidOnFatalError(e);
240                    }
241            }
242            private final static byte[] getContacts_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 8};
243            /** Retrieves contact list.  * @return contacts
244             */
245            public Contact[] getContacts() {
246                    try {
247                            return (Contact[]) sidDoRequest(getContacts_req)
248                            .endRequest().getObjectListParm(1, 2, true);
249                    } catch(IOException e) {
250                            mSidRoot.sidOnFatalError(e);
251                            return null
252                    ;}
253            }
254            private final static byte[] canAddContact_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 9};
255            /** Checks if the current user can add given contact to the ContactGroup.   * @param contact Contact to be checked. 
256             * @return result returns true if AddContact(contact) works on this group. 
257             */
258            public boolean canAddContact(Contact contact) {
259                    try {
260                            return sidDoRequest(canAddContact_req)
261                            .addObjectParm(1, contact)
262                            .endRequest().getBoolParm(1, true);
263                    } catch(IOException e) {
264                            mSidRoot.sidOnFatalError(e);
265                            return false
266                    ;}
267            }
268            private final static byte[] addContact_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 10};
269            /** Adds contact to a contact group. This only works for non-hardwired contact groups.  * @param contact
270             */
271            public void addContact(Contact contact) {
272                    try {
273                            sidDoRequest(addContact_req)
274                            .addObjectParm(1, contact)
275                            .endOneWay();
276                    } catch(IOException e) {
277                            mSidRoot.sidOnFatalError(e);
278                    }
279            }
280            private final static byte[] canRemoveContact_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 11};
281            /** Checks if the current user can remove given contact from the ContactGroup.  * @return result true if RemoveContact(contact) works on this group
282             */
283            public boolean canRemoveContact() {
284                    try {
285                            return sidDoRequest(canRemoveContact_req)
286                            .endRequest().getBoolParm(1, true);
287                    } catch(IOException e) {
288                            mSidRoot.sidOnFatalError(e);
289                            return false
290                    ;}
291            }
292            private final static byte[] removeContact_req = {(byte) 90,(byte) 82,(byte) 10,(byte) 12};
293            /** Removes contact from the ContactGroup.  * @param contact
294             */
295            public void removeContact(Contact contact) {
296                    try {
297                            sidDoRequest(removeContact_req)
298                            .addObjectParm(1, contact)
299                            .endOneWay();
300                    } catch(IOException e) {
301                            mSidRoot.sidOnFatalError(e);
302                    }
303            }
304            /***
305             * generic multiget of a list of Property
306             * @param requested the list of requested properties of ContactGroup
307             * @return SidGetResponding
308             */
309            public SidGetResponding sidMultiGet(Property[] requested) {
310                    return super.sidMultiGet(requested);
311            }
312            /***
313             * generic multiget of list of Property for a list of ContactGroup
314             * @param requested the list of requested properties
315             * @return SidGetResponding[] can be casted to (ContactGroup[]) if all properties are cached
316             */
317            static public SidGetResponding[] sidMultiGet(Property[] requested, ContactGroup[] objects) {
318                    return SidObject.sidMultiGet(requested, objects);
319            }
320            /** ContactGroup.TYPE */
321            public Type getType() {
322                    synchronized(this) {                    if ((mSidCached & 0x1) != 0)
323                                    return mType;
324                    }
325                    return (Type) sidRequestEnumProperty(Property.P_TYPE);
326            }
327            /** unique 32-bit ID for custom groups */
328            public int getCustomGroupId() {
329                    synchronized(this) {
330                            if ((mSidCached & 0x2) != 0)
331                                    return mCustomGroupId;
332                    }
333                    return sidRequestUintProperty(Property.P_CUSTOM_GROUP_ID);
334            }
335            /** change via ContactGroup.GiveDisplayname() */
336            public String getGivenDisplayName() {
337                    synchronized(this) {
338                            if ((mSidCached & 0x4) != 0)
339                                    return mGivenDisplayName;
340                    }
341                    return sidRequestStringProperty(Property.P_GIVEN_DISPLAY_NAME);
342            }
343            /** Number of contacts in the group. NB! The value of this property does not get updated until 5 seconds after account login. During these initial 5 seconds, the value of this property remains 0. The reason for this 5 second delay is to reduce the flurry of property update traffic that occurs during the CBL synchronization phase, following successful login. Note that if you absolutely need to have this value immediately after login, you can still get it by retrieving the contact list with ContactGroup.GetContacts method and examining its size.   */
344            public int getContactCount() {
345                    synchronized(this) {
346                            if ((mSidCached & 0x8) != 0)
347                                    return mContactCount;
348                    }
349                    return sidRequestUintProperty(Property.P_CONTACT_COUNT);
350            }
351            /** number of contacts online in the group */
352            public int getOnlineContactCount() {
353                    synchronized(this) {
354                            if ((mSidCached & 0x10) != 0)
355                                    return mOnlineContactCount;
356                    }
357                    return sidRequestUintProperty(Property.P_ONLINE_CONTACT_COUNT);
358            }
359            public String sidGetStringProperty(final PropertyEnumConverting prop) {
360                    assert(prop.getId() == 151);
361                    return mGivenDisplayName;
362            }
363            public int sidGetIntProperty(final PropertyEnumConverting prop) {
364                    switch(prop.getId()) {
365                    case 154:
366                            return mCustomGroupId;
367                    case 152:
368                            return mContactCount;
369                    case 153:
370                            return mOnlineContactCount;
371                    }
372                    return 0;
373            }
374            public EnumConverting sidGetEnumProperty(final PropertyEnumConverting prop) {
375                    assert(prop.getId() == 155);
376                    return mType;
377            }
378            public String getPropertyAsString(final int prop) {
379                    switch (prop) {
380                    case 155: return getType().toString();
381                    case 154: return Integer.toString(getCustomGroupId());
382                    case 151: return getGivenDisplayName();
383                    case 152: return Integer.toString(getContactCount());
384                    case 153: return Integer.toString(getOnlineContactCount());
385                    }
386                    return "<unkown>";
387            }
388            public String getPropertyAsString(final Property prop) {
389                    return getPropertyAsString(prop.getId());
390            }
391            protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue) {
392                    final Property property = Property.get(propertyId);
393                    if (property == Property.P_UNKNOWN) return;
394                    final int idx = property.getIdx();
395                    if (idx != 0) {
396                            int bit  = 1<<((idx-1)%32);
397                            synchronized (this) {
398                                    mSidCached|=bit;
399                                    switch (propertyId) {
400                                    case 155: mType = Type.get(value); break;
401                                    case 154: mCustomGroupId = value; break;
402                                    case 151:
403                                            if (svalue != null) mGivenDisplayName = svalue;
404                                            else mSidCached &=~bit;
405                                            break;
406                                    case 152: mContactCount = value; break;
407                                    case 153: mOnlineContactCount = value; break;
408                                    default: mSidCached|=bit; break;
409                                    }
410                            }
411                    }
412                    ContactGroupListener listener = ((Skype) mSidRoot).getContactGroupListener();
413                    if (listener != null)
414                            listener.onPropertyChange(this, property, value, svalue);
415            }
416            public void sidSetProperty(final PropertyEnumConverting prop, final String newValue) {
417                    final int propId = prop.getId();
418                    assert(propId == 151);
419                    mSidCached |= 0x4;
420                    mGivenDisplayName=  newValue;
421            }
422            public void sidSetProperty(final PropertyEnumConverting prop, final int newValue) {
423                    final int propId = prop.getId();
424                    switch(propId) {
425                    case 155:
426                            mSidCached |= 0x1;
427                            mType= Type.get(newValue);
428                            break;
429                    case 154:
430                            mSidCached |= 0x2;
431                            mCustomGroupId=  newValue;
432                            break;
433                    case 152:
434                            mSidCached |= 0x8;
435                            mContactCount=  newValue;
436                            break;
437                    case 153:
438                            mSidCached |= 0x10;
439                            mOnlineContactCount=  newValue;
440                            break;
441                    }
442            }
443            public Type   mType;
444            public int    mCustomGroupId;
445            public String mGivenDisplayName;
446            public int    mContactCount;
447            public int    mOnlineContactCount;
448            public int moduleId() {
449                    return 10;
450            }
451            
452            public ContactGroup(final int oid, final SidRoot root) {
453                    super(oid, root, 5);
454            }
455    }