001    package com.skype.api;
002    
003    import java.io.IOException;
004    import java.util.*;
005    import com.skype.ipc.*;
006    /**
007     * This class encapsulates functionality for looking up contacts on the Skype network. Contacts can be searched by portion of their name, e-mail address, language preferences, etc. <br><br>Contact search is asynchronous. ContactSearch.Submit is a non-blocking function that initiates the search. Upon finding a matching contact, ContactSearch.OnNewResult event gets fired, that gives you the reference to the discovered contact. You can get up to 100 matching contacts per search. Note that you will need to keep a live reference of the ContactSearch object while the search is doing its work.  <br><br>So, to perform a contact search:  <br> - create a contact search object <br> - specify search terms and conditions <br> - submit search <br> - in ContactSearch.OnNewResult callback, update your UI <br> - in ContactSearch.OnChange, check for terminal values of P_CONTACT_SEARCH_STATUS and update the UI accordingly. <br><br>When the search has done its job, the ContactSearch.P_CONTACT_SEARCH_STATUS property will go to one of the terminal values. <br><br>The terminal values are: <br> - FINISHED - the search has stopped. Note that this does not mean any matches were actually found. <br> - FAILED - the search has failed. <br> - EXTENDABLE - this state should be considered the same as FINISHED. The feature of extending long search results is about to be deprecated. It is still possible for search objects to occasionally reach that state, so it should be handled in the UI (as FINISHED), but the extending feature itself should not be implemented in your UI. <br><br>There are three methods to create the ContactSearch objects. <br><br>A) Skype.CreateIdentitySearch <br><br>This method takes a string argument and looks for exact matches against Contact.P_SKYPENAME property. So for example, identity search for "echo" will return 0 results and search for "echo123" will return exactly one.  <br><br>Identity in this case means skypename - contact search does not work with PSTN type contacts. However, it does work for SKYPE type contacts that have supplied P_PHONE_HOME, P_PHONE_OFFICE or P_PHONE_MOBILE values in their account data. To search for those, you will need to use complex search (see below). <br><br>Note that you should always check for boolean return value of the CreateIdentitySearch method. If the user submits a string that is not a valid skypename, the method will return false and the ContactSearchRef argument will return as NULL. <br><br>B) Skype.CreateBasicContactSearch <br><br>This method takes a string argument and looks for non-exact matches against both P_SKYPENAME and P_FULLNAME properties of the contact. If you intend to implement a simple, one-input search feature - this is the best method for you. The non-exact matching operates similarly to the SQL LIKE condition. <br><br>C) Skype.CreateContactSearch <br><br>This method enables you to implement advanced contact search, matching against multiple seach criteria. It takes no input arguments and expects search criteria to be added to the already constructed search object. <br><br>Criteria can be added with ContactSearch.AddStrTerm and ContactSearch.AddIntTerm methods. <br><br>These methods take Contact class porperty ID, condition, and the match pattern as inputs. <br><br>Only the following Contact properties can be used for search: <br> - P_SKYPENAME  <br> - P_FULLNAME <br> - P_BIRTHDAY (uint) <br> - P_GENDER (uint: 1-male, 2-female) <br> - P_LANGUAGES <br> - P_COUNTRY <br> - P_PROVINCE <br> - P_CITY <br> - P_PHONE_HOME <br> - P_PHONE_OFFICE <br> - P_PHONE_MOBILE <br> - P_EMAILS <br> - P_HOMEPAGE <br> - P_ABOUT <br><br>String searches are case insensitive, i.e. search for echo123 also matches ECHO123 <br><br>When adding multiple criteria, default behaviour is that the criterions are additive. I.e. a term skypename == "joe" followed by term country == "us" will result in intersection between all joes and everybody in US. <br><br>You can explicitly add an "OR" instead of "AND" between conditions, using the AddOr method. <br><br>By default, AND criteria are grouped together, before OR's, so that: <br><br>AddTerm(condition1) <br>AddTerm(condition2) <br>AddOr() <br>AddTerm(condition3) <br>AddTerm(condition4) <br><br>will result in the following logical statement: <br>(condition1 AND condition2) OR (condition3 AND condition4) <br><br>However, you can add "global" critera, by using the add_to_subs argument of the AddXX methods. <br><br>AddTerm(condition1) <br>AddTerm(condition2) <br>AddOr() <br>AddTerm(condition3) <br>AddTerm(condition4, add_to_subs=true) <br><br>which would result in: <br>(condition1 AND condition2 AND condition4) OR (condition3 AND condition4) <br><br><br>Every one of the contact properties can only be used once, per search. For example, you cannot create a search for two different P_FULLNAME patterns. The &valid argument will still return tue if you do this, but the last criteria for any given property will override all previous ones. So, a search like this: <br><br>cs->AddStrTerm(Contact.P_FULLNAME, ContactSearch.EQ, "John Smith", isValid); <br>cs->AddOr(); <br>cs->AddStrTerm(Contact.P_FULLNAME, ContactSearch.EQ, "Ivan Sidorov", isValid); <br><br>will only return matches for "Ivan Sidorov" and none for "John Smith". <br><br>Some of the contact properties are automatically combined for purposes of search. <br><br>A search for P_SKYPENAME also returns matches from the P_FULLNAME property and vice versa. <br><br>So that this: <br>cs->AddStrTerm(Contact.P_SKYPENAME, ContactSearch.EQ, "john.smith", isValid); <br><br>..and this: <br>cs->AddStrTerm(Contact.P_FULLNAME, ContactSearch.EQ, "John Smith", isValid); <br><br>..and this: <br>cs->AddStrTerm(Contact.P_SKYPENAME, ContactSearch.EQ, "john.smith", isValid); <br>cs->AddOr(); <br>cs->AddStrTerm(Contact.P_FULLNAME, ContactSearch.EQ, "John Smith", isValid); <br><br>..all search from both the P_FULLNAME and P_SKYPENAME fields. <br><br><br>Before using ContactGroup.Submit to start the search, you should always check whether the search criteria ended up being valid. This you can do with ContactSearch.IsValid method. <br><br>As you probably noticed, each of the AddXX methods also return a validity check boolean. However, it is a better practice to do the overall check as well, even if all the individual search criteria ended up looking Ok. <br><br>For example, lets take a search for contact's e-mail. This can be done with two different methods. Firstly we can use the ContactSearch.AddEmailTerm method. This method will actually validate whether the input is a valid e-mail address: <br><br>cs->AddEmailTerm ("test@test@test", isValid);  <br>will return the isValid argument as false. <br><br>However, you can also add the e-mail search criterion as a simple string, like this: <br><br>cs->AddStrTerm(Contact.P_EMAILS, ContactSearch.EQ, "test@test@test@", isValid); <br>in which case the isValid will return true. <br><br>However, if you then check entire search object with:  <br><br>cs->IsValid(isValid); <br><br>the isValid will correctly return false. <br>
008     */
009    
010    
011    public class ContactSearch extends SkypeObject {
012    
013    
014            public interface ContactSearchListener {
015                    /** This event gets called when there are changes to ContactSearch properties defined in ContactSearch.PROPERTY  */
016                    public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
017                    
018                    /**This callback is fired when a new matching contact has been found during the search. <br>*/
019                    public void OnNewResult(SkypeObject obj, Contact contact, int rankValue);
020                    
021            }
022            
023            public ContactSearch(int oid, Skype skype) {
024                    super(oid,skype);
025            }
026            
027            private static final int MODULE_ID = 1;
028            
029            public static final int moduleID() {
030                    return MODULE_ID;
031            }
032            
033            /** Properties of the ContactSearch class */
034            public enum PROPERTY {
035            
036                    /** type: STATUS */
037                    contact_search_status(200);
038                    
039                    private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
040                    
041                    static {
042                            for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
043                                    lookup.put(s.getId(), s);
044                    }
045                    
046                    private final int id;
047                    
048                    private PROPERTY(int value) {
049                            this.id = value;
050                    }
051                    
052                    public int getId() { return id; }
053                    
054                    public static PROPERTY get(int code) {
055                            return lookup.get(code);
056                    }
057                    
058                    public static PROPERTY fromString(String s) {
059                            for (PROPERTY p : lookup.values()) {
060                                    if (p.toString() == s) {
061                                            return p;
062                                    }
063                            }
064                            return null;
065                    }
066            }
067            
068            public Object GetPropertyAsEnum(int propid) {
069                    return PROPERTY.get(propid);
070            }
071            
072            public String GetStrProperty(PROPERTY prop) {
073                    //check in propcache if so then return
074                    if (mPropCache.containsKey(new Integer(prop.id))){
075                            String value =  (String)mPropCache.get(prop.id);
076                            if (value != null && !(value.length() == 0) ){
077                                    return value;
078                            }
079                    }
080                    //else get from skypekit...
081                    GetPropertyRequest request = new GetPropertyRequest(1, mObjectId, prop.id);
082                    
083                    String string = null;
084                    GetPropertyResponse r = skype.GetProperty(request);
085                    if (r != null){
086                            string = r.GetAsString();
087                    }
088                    
089                    if (string != null)
090                    {
091                            mPropCache.put(new Integer(prop.id), string);
092                    }
093                    return string;
094            }
095            
096            public int GetIntProperty(PROPERTY prop) {
097                    //check in propcache if so then return
098                    if (mPropCache.containsKey(new Integer(prop.id))){
099                            int value = ((Integer)mPropCache.get(prop.id)).intValue();
100                            if (value != 0){
101                                    return value;
102                            }
103                    }
104                    //else get from skypekit...
105                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
106                    
107                    Integer integer = null;
108                    GetPropertyResponse r = skype.GetProperty(request);
109                    if (r != null){
110                            integer  = r.GetAsInt();
111                    }
112                    
113                    if (integer != null)
114                    {
115                            mPropCache.put(new Integer(prop.id), integer);
116                            return integer.intValue();
117                    }
118                    else
119                    {
120                            return 0;
121                    }
122            }
123            
124            public boolean GetBooleanProperty(PROPERTY prop) {
125                    //check in propcache if so then return
126                    if (mPropCache.containsKey(new Integer(prop.id))){
127                            return ((Boolean)mPropCache.get(prop.id)).booleanValue();
128                    }
129                    //else get from skypekit...
130                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
131                    
132                    Boolean boolResp = null;
133                    GetPropertyResponse r = skype.GetProperty(request);
134                    if (r != null){
135                            boolResp  = r.GetAsBoolean();
136                    }
137                    
138                    if (boolResp != null)
139                    {
140                            mPropCache.put(new Integer(prop.id), boolResp);
141                            return boolResp.booleanValue();
142                    }
143                    else
144                    {
145                            return false;
146                    }
147            }
148            
149            public byte [] GetBinProperty(PROPERTY prop) {
150                    //get from skypekit...
151                    GetPropertyRequest request = new GetPropertyRequest(1, mObjectId, prop.id);
152                    
153                    byte [] data = null;
154                    GetPropertyResponse r = skype.GetProperty(request);
155                    if (r != null) {
156                            data = r.GetAsBinary();
157                    }
158                    return data;
159            }
160            
161            /**
162            Possible values for the ContactSearch.P_STATUS property. <br> */
163            public enum STATUS {
164            
165                    /** Transient state, obtained after submission and actually initiating the search on the network. <br>*/
166                    CONSTRUCTION(1),
167                    
168                    /** Waiting for results to show up. This is a transient state. <br>*/
169                    PENDING(2),
170                    
171                    /** Enough matches are found. No more OnNewResult events will fire. The feature of extending long search results is about to be deprecated. It is still possible for search objects to occasionally reach that state, so it should be handled in the UI (as FINISHED), but the extending feature itself should not be implemented in your UI.  <br>*/
172                    EXTENDABLE(3),
173                    
174                    /** The search is finished. No more matches are expected. This is a terminal state. <br>*/
175                    FINISHED(4),
176                    
177                    /** ContactSearch failed. Better check if the search terms made any sense, with ContactSearch.IsValid. This is a terminal state. <br>*/
178                    FAILED(5);
179                    
180                    private static final Map<Integer,STATUS> lookup = new HashMap<Integer,STATUS>();
181                    
182                    static {
183                            for(STATUS s : EnumSet.allOf(STATUS.class))
184                                    lookup.put(s.getId(), s);
185                    }
186                    
187                    private final int id;
188                    
189                    private STATUS(int value) {
190                            this.id = value;
191                    }
192                    
193                    public int getId() { return id; }
194                    
195                    public static STATUS get(int code) {
196                            return lookup.get(code);
197                    }
198                    
199                    public static STATUS fromString(String s) {
200                            for (STATUS p : lookup.values()) {
201                                    if (p.toString() == s) {
202                                            return p;
203                                    }
204                            }
205                            return null;
206                    }
207            }
208            
209            /**
210             *construct CONTACT_BIRTHDAY term based on current time
211             * @param min_age_in_years
212             * @param add_to_subs
213             * @return valid
214             */
215            public boolean AddMinAgeTerm( int min_age_in_years, boolean add_to_subs) {
216            
217                    Request request = null;
218                    try {
219                            request = new XCallRequest(1,1);
220                    } catch (IOException e) {
221                            e.printStackTrace();
222                            if (skype.errorListener != null)
223                                    skype.errorListener.OnSkypeKitFatalError();
224                    }
225                    request.addParm('O',0,mObjectId);
226                    request.addParm('u',1,min_age_in_years);
227                    request.addParm('b',2,add_to_subs);
228                    
229                    Response r = skype.XCall((XCallRequest)request);
230                    
231                    if (r.isErrCall())
232                            return false;
233                            
234                    boolean valid = false;
235                    valid = r.GetAsBoolean(1);
236                    return valid;
237            }
238            
239            /**
240             *construct CONTACT_BIRTHDAY term based on current time
241             * @param max_age_in_years
242             * @param add_to_subs
243             * @return valid
244             */
245            public boolean AddMaxAgeTerm( int max_age_in_years, boolean add_to_subs) {
246            
247                    Request request = null;
248                    try {
249                            request = new XCallRequest(1,2);
250                    } catch (IOException e) {
251                            e.printStackTrace();
252                            if (skype.errorListener != null)
253                                    skype.errorListener.OnSkypeKitFatalError();
254                    }
255                    request.addParm('O',0,mObjectId);
256                    request.addParm('u',1,max_age_in_years);
257                    request.addParm('b',2,add_to_subs);
258                    
259                    Response r = skype.XCall((XCallRequest)request);
260                    
261                    if (r.isErrCall())
262                            return false;
263                            
264                    boolean valid = false;
265                    valid = r.GetAsBoolean(1);
266                    return valid;
267            }
268            
269            /**
270             *Adds a search term against Contact.P_EMAILS property and pre-validates the value given in the email argument. <br>
271             * @param email e-mail addres to search for. <br>
272             * @param add_to_subs This argument enables you to group conditions. See ContactSearch class details for more information. <br>
273             * @return valid Returns false if the value in email property did not look like a valid email address. <br>
274             */
275            public boolean AddEmailTerm( String email, boolean add_to_subs) {
276            
277                    Request request = null;
278                    try {
279                            request = new XCallRequest(1,3);
280                    } catch (IOException e) {
281                            e.printStackTrace();
282                            if (skype.errorListener != null)
283                                    skype.errorListener.OnSkypeKitFatalError();
284                    }
285                    request.addParm('O',0,mObjectId);
286                    request.addParm('S',1,email);
287                    request.addParm('b',2,add_to_subs);
288                    
289                    Response r = skype.XCall((XCallRequest)request);
290                    
291                    if (r.isErrCall())
292                            return false;
293                            
294                    boolean valid = false;
295                    valid = r.GetAsBoolean(1);
296                    return valid;
297            }
298            
299            /**
300             * @param language
301             * @param add_to_subs
302             * @return valid
303             */
304            public boolean AddLanguageTerm( String language, boolean add_to_subs) {
305            
306                    Request request = null;
307                    try {
308                            request = new XCallRequest(1,4);
309                    } catch (IOException e) {
310                            e.printStackTrace();
311                            if (skype.errorListener != null)
312                                    skype.errorListener.OnSkypeKitFatalError();
313                    }
314                    request.addParm('O',0,mObjectId);
315                    request.addParm('S',1,language);
316                    request.addParm('b',2,add_to_subs);
317                    
318                    Response r = skype.XCall((XCallRequest)request);
319                    
320                    if (r.isErrCall())
321                            return false;
322                            
323                    boolean valid = false;
324                    valid = r.GetAsBoolean(1);
325                    return valid;
326            }
327            
328            /**
329            List of available  matching conditions that can be used in AddTerm methods. <br> */
330            public enum CONDITION {
331            
332                    /** Equals <br>*/
333                    EQ(0),
334                    
335                    /** Is greater than <br>*/
336                    GT(1),
337                    
338                    /** Is greater or equal. <br>*/
339                    GE(2),
340                    
341                    /** Is less than <br>*/
342                    LT(3),
343                    
344                    /** Less or equal <br>*/
345                    LE(4),
346                    
347                    /** Start of a word macthes exactly (string properties only). <br>*/
348                    PREFIX_EQ(5),
349                    
350                    /** Start of a word is greater or equal (string properties only). <br>*/
351                    PREFIX_GE(6),
352                    
353                    /** Start of a word is less or equal (string properties only). <br>*/
354                    PREFIX_LE(7),
355                    
356                    /** Contains the word (string properties only). <br>*/
357                    CONTAINS_WORDS(8),
358                    
359                    /** One of the words starts with searched value (string properties only). <br>*/
360                    CONTAINS_WORD_PREFIXES(9);
361                    
362                    private static final Map<Integer,CONDITION> lookup = new HashMap<Integer,CONDITION>();
363                    
364                    static {
365                            for(CONDITION s : EnumSet.allOf(CONDITION.class))
366                                    lookup.put(s.getId(), s);
367                    }
368                    
369                    private final int id;
370                    
371                    private CONDITION(int value) {
372                            this.id = value;
373                    }
374                    
375                    public int getId() { return id; }
376                    
377                    public static CONDITION get(int code) {
378                            return lookup.get(code);
379                    }
380                    
381                    public static CONDITION fromString(String s) {
382                            for (CONDITION p : lookup.values()) {
383                                    if (p.toString() == s) {
384                                            return p;
385                                    }
386                            }
387                            return null;
388                    }
389            }
390            
391            /**
392             *Adds a string search term to a custom contact search object.  <br>
393             * @param prop Following Contact class string propkeys can be used for Contact search: <br> - Contact.P_SKYPENAME <br> - Contact.P_FULLNAME <br> - Contact.P_LANGUAGES <br> - Contact.P_COUNTRY <br> - Contact.P_PROVINCE <br> - Contact.P_CITY <br> - Contact.P_PHONE_HOME <br> - Contact.P_PHONE_OFFICE <br> - Contact.P_PHONE_MOBILE <br> - Contact.P_EMAILS <br> - Contact.P_HOMEPAGE <br> - Contact.P_ABOUT <br>Note that while Contact.P_EMAILS is technically a string and can be used in this method, it is recommended that you use ContactSearch.AddEmailTerm method instead. <br>
394             * @param cond Search condition (ContactSearch.CONDITION) <br>
395             * @param value Value to match against. <br>
396             * @param add_to_subs This argument enables you to group conditions. See ContactSearch class details for more information. <br>
397             * @return valid Returns true if the ContactSearch term-set remains valid after adding this term. <br>
398             */
399            public boolean AddStrTerm( int prop, CONDITION cond, String value, boolean add_to_subs) {
400            
401                    Request request = null;
402                    try {
403                            request = new XCallRequest(1,5);
404                    } catch (IOException e) {
405                            e.printStackTrace();
406                            if (skype.errorListener != null)
407                                    skype.errorListener.OnSkypeKitFatalError();
408                    }
409                    request.addParm('O',0,mObjectId);
410                    request.addParm('e',1,prop);
411                    request.addParm('e',2,cond.getId());
412                    request.addParm('S',3,value);
413                    request.addParm('b',4,add_to_subs);
414                    
415                    Response r = skype.XCall((XCallRequest)request);
416                    
417                    if (r.isErrCall())
418                            return false;
419                            
420                    boolean valid = false;
421                    valid = r.GetAsBoolean(1);
422                    return valid;
423            }
424            
425            /**
426             *Adds a search term to a custom contact search object. For now, there are only two searchable Contact properties that are integers, so this can oly be used for Contact.P_BIRTHDAY and Contact.P_GENDER. <br>
427             * @param prop Propkey to search for. Either Contact.P_BIRTHDAY or Contact.P_GENDER <br>
428             * @param cond Search condition (ContactSearch.CONDITION) <br>
429             * @param value Value to match against. <br>
430             * @param add_to_subs This argument enables you to group conditions. See ContactSearch class details for more information. <br>
431             * @return valid Returns true if the ContactSearch term-set remains valid after adding this term. <br>
432             */
433            public boolean AddIntTerm( int prop, CONDITION cond, int value, boolean add_to_subs) {
434            
435                    Request request = null;
436                    try {
437                            request = new XCallRequest(1,6);
438                    } catch (IOException e) {
439                            e.printStackTrace();
440                            if (skype.errorListener != null)
441                                    skype.errorListener.OnSkypeKitFatalError();
442                    }
443                    request.addParm('O',0,mObjectId);
444                    request.addParm('e',1,prop);
445                    request.addParm('e',2,cond.getId());
446                    request.addParm('u',3,value);
447                    request.addParm('b',4,add_to_subs);
448                    
449                    Response r = skype.XCall((XCallRequest)request);
450                    
451                    if (r.isErrCall())
452                            return false;
453                            
454                    boolean valid = false;
455                    valid = r.GetAsBoolean(1);
456                    return valid;
457            }
458            
459            /**
460             *used to group terms (AddTerm(1), AddTerm(2), Or(), AddTerm(3), AddTerm(4), etc)
461             */
462            public void AddOr() {
463            
464                    Request request = null;
465                    try {
466                            request = new XCallRequest(1,7);
467                    } catch (IOException e) {
468                            e.printStackTrace();
469                            if (skype.errorListener != null)
470                                    skype.errorListener.OnSkypeKitFatalError();
471                    }
472                    request.addParm('O',0,mObjectId);
473                    
474                    skype.XCall((XCallRequest)request);
475            }
476            
477            /**
478             *checks that terms list is non-empty and does not contain unsupported keys
479             * @return result
480             */
481            public boolean IsValid() {
482            
483                    Request request = null;
484                    try {
485                            request = new XCallRequest(1,8);
486                    } catch (IOException e) {
487                            e.printStackTrace();
488                            if (skype.errorListener != null)
489                                    skype.errorListener.OnSkypeKitFatalError();
490                    }
491                    request.addParm('O',0,mObjectId);
492                    
493                    Response r = skype.XCall((XCallRequest)request);
494                    
495                    if (r.isErrCall())
496                            return false;
497                            
498                    boolean result = false;
499                    result = r.GetAsBoolean(1);
500                    return result;
501            }
502            
503            /**
504             *launch search
505             */
506            public void Submit() {
507            
508                    Request request = null;
509                    try {
510                            request = new XCallRequest(1,9);
511                    } catch (IOException e) {
512                            e.printStackTrace();
513                            if (skype.errorListener != null)
514                                    skype.errorListener.OnSkypeKitFatalError();
515                    }
516                    request.addParm('O',0,mObjectId);
517                    
518                    skype.XCall((XCallRequest)request);
519            }
520            
521            /**
522             *extend if search is EXTENDABLE
523             */
524            public void Extend() {
525            
526                    Request request = null;
527                    try {
528                            request = new XCallRequest(1,10);
529                    } catch (IOException e) {
530                            e.printStackTrace();
531                            if (skype.errorListener != null)
532                                    skype.errorListener.OnSkypeKitFatalError();
533                    }
534                    request.addParm('O',0,mObjectId);
535                    
536                    skype.XCall((XCallRequest)request);
537            }
538            
539            /**
540             *releases results that are not referenced from elsewhere
541             */
542            public void Release() {
543            
544                    Request request = null;
545                    try {
546                            request = new XCallRequest(1,12);
547                    } catch (IOException e) {
548                            e.printStackTrace();
549                            if (skype.errorListener != null)
550                                    skype.errorListener.OnSkypeKitFatalError();
551                    }
552                    request.addParm('O',0,mObjectId);
553                    
554                    skype.XCall((XCallRequest)request);
555            }
556            
557            /**
558             *result list is dynamically updated
559             * @param from
560             * @param count
561             * @return contacts
562             */
563            public Contact [] GetResults( int from, int count) {
564            
565                    Request request = null;
566                    try {
567                            request = new XCallRequest(1,11);
568                    } catch (IOException e) {
569                            e.printStackTrace();
570                            if (skype.errorListener != null)
571                                    skype.errorListener.OnSkypeKitFatalError();
572                    }
573                    request.addParm('O',0,mObjectId);
574                    request.addParm('u',1,from);
575                    request.addParm('u',2,count);
576                    
577                    Response r = skype.XCall((XCallRequest)request);
578                    
579                    if (r.isErrCall())
580                            return null;
581                            
582                    Vector<Contact> contacts = new Vector<Contact>();
583                    while (r.HasMore(1))
584                    {
585                            int oid = 0;
586                            Contact contact = null;
587                            oid = r.GetOid(1);
588                            if (oid != AbstractDecoder.NULL_VALUE) { 
589                                    contact = (Contact)skype.factory(Contact.moduleID(), oid, skype);
590                            }
591                            contacts.add(contact);
592                    }
593                    return contacts.toArray(new Contact[contacts.size()]);
594                    
595            }
596            
597    
598    }