    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    }