    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.api.Skype;
    013    import com.skype.ipc.SidGetResponding;
    014
    015    /** Conversation participant class. Instances of this class represent contacts when in the context of conversations. Amongst others, this class has a Ring method for requesting live status with the target contact. This class also holds typing indicator property and access rights for the contact in context of public conversations.  */
    016    public final class Participant extends SidObject {
    017            /** Recognized values for the P_RANK property. The P_RANK controls participant's privileges in the conversation. See Participant.CanSetRankTo and Participant.SetRankTo methods.  */
    018            public enum Rank implements EnumConverting {
    019                    /** Creator of the chat. There can be only one participant with this type per conversation. Other participants cannot be promoted to Creator rank.  */
    020                    CREATOR  (1),
    021                    /** Participant who has administrator privileges  */
    022                    ADMIN    (2),
    023                    /** Participant who can speak and write  */
    024                    SPEAKER  (3),
    025                    /** Participant who can write but not speak  */
    026                    WRITER   (4),
    027                    /** Participant who can read but not write/speak  */
    028                    SPECTATOR(5),
    029                    /** Participant who is applying to join the conversation. Member cannot be demoted to applicants once they have been accepted.   */
    030                    APPLICANT(6),
    031                    /** Participant who has eft or has been kicked from the conversation  */
    032                    RETIRED  (7),
    033                    /** Participant who has been banned from the chat  */
    034                    OUTLAW   (8);
    035                    private final int key;
    036                    Rank(int key) {
    037                            this.key = key;
    038                    };
    039                    public int getId()   { return key; }
    040                    public EnumConverting getDefault() { return CREATOR; }
    041                    public EnumConverting convert(int from) { return Rank.get(from); }
    042                    public EnumConverting[] getArray(final int size) { return new Rank[size]; }
    043                    public static Rank get(int from) {
    044                            switch (from) {
    045                            case 1: return CREATOR;
    046                            case 2: return ADMIN;
    047                            case 3: return SPEAKER;
    048                            case 4: return WRITER;
    049                            case 5: return SPECTATOR;
    050                            case 6: return APPLICANT;
    051                            case 7: return RETIRED;
    052                            case 8: return OUTLAW;
    053                            }
    054                            return CREATOR;
    055                    }
    056                    public static final int CREATOR_VALUE   = 1;
    057                    public static final int ADMIN_VALUE     = 2;
    058                    public static final int SPEAKER_VALUE   = 3;
    059                    public static final int WRITER_VALUE    = 4;
    060                    public static final int SPECTATOR_VALUE = 5;
    061                    public static final int APPLICANT_VALUE = 6;
    062                    public static final int RETIRED_VALUE   = 7;
    063                    public static final int OUTLAW_VALUE    = 8;
    064            }
    065            /**
    066             * Recognized values for the P_TEXT_STATUS property. The P_TEXT_STATUS property has two uses. Firstly, you can use it to implement typing indicators in your UI, to notify the local user that an incoming chat message from this Participant is imminent.
    067             *
    068             * To set the P_TEXT_STATUS value, so that remote client UIs can display the local user's typing indicator in their UI, use Conversation.SetMyTextStatusTo method.
    069             *
    070             * Transmission of P_TEXT_STATUS updates to remote participants of conversations is controlled via SETUPKEY_DISABLE_CHAT_ACTIVITY_INDICATION setup key.
    071             *
    072             * Secondly, the TEXT_NA value enables you to detect participants who are running clients with no chat capability.
    073             */
    074            public enum TextStatus implements EnumConverting {
    075                    /** Fallback state in case the text status is not (yet) deternmined.  */
    076                    TEXT_UNKNOWN    (0),
    077                    /** Text status is not applicable as the participant is using a Skype client that does not support chat (for example: voice-only USB phones).  */
    078                    TEXT_NA         (1),
    079                    /** Participant is currently not typing.  */
    080                    READING         (2),
    081                    /** Participant is currently typing.  */
    082                    WRITING         (3),
    083                    /**
    084                     * This state should be set when following two conditions are true:
    085                     *  - interval between keypresses are less than 20 ms;
    086                     *  - at least one of the keys adjacent to current key are pressed down.
    087                     */
    088                    WRITING_AS_ANGRY(4),
    089                    /** The "Cat on keyboard detection" algorthm in Skype is implemented in the UI level, and as such is not present in the SkypeKit API. Should you wish to implement similar algorthm in your own UI, you can get the basic logic from the PawSense FAQ - http://www.bitboost.com/pawsense/pawsense-faq.html  */
    090                    WRITING_AS_CAT  (5);
    091                    private final int key;
    092                    TextStatus(int key) {
    093                            this.key = key;
    094                    };
    095                    public int getId()   { return key; }
    096                    public EnumConverting getDefault() { return TEXT_UNKNOWN; }
    097                    public EnumConverting convert(int from) { return TextStatus.get(from); }
    098                    public EnumConverting[] getArray(final int size) { return new TextStatus[size]; }
    099                    public static TextStatus get(int from) {
    100                            switch (from) {
    101                            case 0: return TEXT_UNKNOWN;
    102                            case 1: return TEXT_NA;
    103                            case 2: return READING;
    104                            case 3: return WRITING;
    105                            case 4: return WRITING_AS_ANGRY;
    106                            case 5: return WRITING_AS_CAT;
    107                            }
    108                            return TEXT_UNKNOWN;
    109                    }
    110                    public static final int TEXT_UNKNOWN_VALUE     = 0;
    111                    public static final int TEXT_NA_VALUE          = 1;
    112                    public static final int READING_VALUE          = 2;
    113                    public static final int WRITING_VALUE          = 3;
    114                    public static final int WRITING_AS_ANGRY_VALUE = 4;
    115                    public static final int WRITING_AS_CAT_VALUE   = 5;
    116            }
    117            /** Recognized values for the P_VOICE_STATUS property.   */
    118            public enum VoiceStatus implements EnumConverting {
    119                    /** Participant voice status is not (yet) determined.  */
    120                    VOICE_UNKNOWN   (0),
    121                    /** Participant is using a Skype client with no audio capability.  */
    122                    VOICE_NA        (1),
    123                    /** Participant is using a Skype client that supports audio.  */
    124                    VOICE_AVAILABLE (2),
    125                    /** Participant is in process of joining current live session. This is a transitional state.  */
    126                    VOICE_CONNECTING(3),
    127                    /** Participant has been invited to join the live session but has not yet accepted.  */
    128                    RINGING         (4),
    129                    /** Participant is in process of joining current live session. This is another transitional state.  */
    130                    EARLY_MEDIA     (5),
    131                    /** Participant has joined the current live session but is currently not transmitting audio.  */
    132                    LISTENING       (6),
    133                    /** Participant has joined the current live session and is transmitting audio. The UI can either use this state to display appropriate "speaking" notification, or alternatively use Participant.P_SOUND_LEVEL if you want your speaking indicator to also reflect audio volume coming from the Participant.  */
    134                    SPEAKING        (7),
    135                    /** Participant has joined the current live session but the audio is currently on hold.  */
    136                    VOICE_ON_HOLD   (8),
    137                    /** Participant will be placed in this state for some seconds after live session has finished. This is another transitional state.  */
    138                    VOICE_STOPPED   (9);
    139                    private final int key;
    140                    VoiceStatus(int key) {
    141                            this.key = key;
    142                    };
    143                    public int getId()   { return key; }
    144                    public EnumConverting getDefault() { return VOICE_UNKNOWN; }
    145                    public EnumConverting convert(int from) { return VoiceStatus.get(from); }
    146                    public EnumConverting[] getArray(final int size) { return new VoiceStatus[size]; }
    147                    public static VoiceStatus get(int from) {
    148                            switch (from) {
    149                            case 0: return VOICE_UNKNOWN;
    150                            case 1: return VOICE_NA;
    151                            case 2: return VOICE_AVAILABLE;
    152                            case 3: return VOICE_CONNECTING;
    153                            case 4: return RINGING;
    154                            case 5: return EARLY_MEDIA;
    155                            case 6: return LISTENING;
    156                            case 7: return SPEAKING;
    157                            case 8: return VOICE_ON_HOLD;
    158                            case 9: return VOICE_STOPPED;
    159                            }
    160                            return VOICE_UNKNOWN;
    161                    }
    162                    public static final int VOICE_UNKNOWN_VALUE    = 0;
    163                    public static final int VOICE_NA_VALUE         = 1;
    164                    public static final int VOICE_AVAILABLE_VALUE  = 2;
    165                    public static final int VOICE_CONNECTING_VALUE = 3;
    166                    public static final int RINGING_VALUE          = 4;
    167                    public static final int EARLY_MEDIA_VALUE      = 5;
    168                    public static final int LISTENING_VALUE        = 6;
    169                    public static final int SPEAKING_VALUE         = 7;
    170                    public static final int VOICE_ON_HOLD_VALUE    = 8;
    171                    public static final int VOICE_STOPPED_VALUE    = 9;
    172            }
    173            /** Recognized values for the P_VIDEO_STATUS property. This property applies to Participant's video send capability, not capability to receive video.  */
    174            public enum VideoStatus implements EnumConverting {
    175                    /** Video status is not (yet) determined.  */
    176                    VIDEO_UNKNOWN   (0),
    177                    /** Indicates that this Participant does not have video available..  */
    178                    VIDEO_NA        (1),
    179                    /** Indicates that video is available for this participant. When the Participant.P_VIDEO_STATUS obtains this state, it is possible to retrieve the Video object, using Participant.GetVideo method. Further operations, such as starting or stopping the video send/receive will then go through the Video object.  */          VIDEO_AVAILABLE (2),
    180                    /** Transitional state indicating that the Participant is attempting to initiate video send.  */
    181                    VIDEO_CONNECTING(3),
    182                    /** Indicates that the participant is currently sending video.  */
    183                    STREAMING       (4),
    184                    /** Indicates that the participant video send is currently paused.  */
    185                    VIDEO_ON_HOLD   (5);
    186                    private final int key;
    187                    VideoStatus(int key) {
    188                            this.key = key;
    189                    };
    190                    public int getId()   { return key; }
    191                    public EnumConverting getDefault() { return VIDEO_UNKNOWN; }
    192                    public EnumConverting convert(int from) { return VideoStatus.get(from); }
    193                    public EnumConverting[] getArray(final int size) { return new VideoStatus[size]; }
    194                    public static VideoStatus get(int from) {
    195                            switch (from) {
    196                            case 0: return VIDEO_UNKNOWN;
    197                            case 1: return VIDEO_NA;
    198                            case 2: return VIDEO_AVAILABLE;
    199                            case 3: return VIDEO_CONNECTING;
    200                            case 4: return STREAMING;
    201                            case 5: return VIDEO_ON_HOLD;
    202                            }
    203                            return VIDEO_UNKNOWN;
    204                    }
    205                    public static final int VIDEO_UNKNOWN_VALUE    = 0;
    206                    public static final int VIDEO_NA_VALUE         = 1;
    207                    public static final int VIDEO_AVAILABLE_VALUE  = 2;
    208                    public static final int VIDEO_CONNECTING_VALUE = 3;
    209                    public static final int STREAMING_VALUE        = 4;
    210                    public static final int VIDEO_ON_HOLD_VALUE    = 5;
    211            }
    212            public enum Dtmf implements EnumConverting {
    213                    DTMF_0    (0),
    214                    DTMF_1    (1),
    215                    DTMF_2    (2),
    216                    DTMF_3    (3),
    217                    DTMF_4    (4),
    218                    DTMF_5    (5),
    219                    DTMF_6    (6),
    220                    DTMF_7    (7),
    221                    DTMF_8    (8),
    222                    DTMF_9    (9),
    223                    DTMF_STAR (10),
    224                    DTMF_POUND(11);
    225                    private final int key;
    226                    Dtmf(int key) {
    227                            this.key = key;
    228                    };
    229                    public int getId()   { return key; }
    230                    public EnumConverting getDefault() { return DTMF_0; }
    231                    public EnumConverting convert(int from) { return Dtmf.get(from); }
    232                    public EnumConverting[] getArray(final int size) { return new Dtmf[size]; }
    233                    public static Dtmf get(int from) {
    234                            switch (from) {
    235                            case  0: return DTMF_0;
    236                            case  1: return DTMF_1;
    237                            case  2: return DTMF_2;
    238                            case  3: return DTMF_3;
    239                            case  4: return DTMF_4;
    240                            case  5: return DTMF_5;
    241                            case  6: return DTMF_6;
    242                            case  7: return DTMF_7;
    243                            case  8: return DTMF_8;
    244                            case  9: return DTMF_9;
    245                            case 10: return DTMF_STAR;
    246                            case 11: return DTMF_POUND;
    247                            }
    248                            return DTMF_0;
    249                    }
    250                    public static final int DTMF_0_VALUE     =  0;
    251                    public static final int DTMF_1_VALUE     =  1;
    252                    public static final int DTMF_2_VALUE     =  2;
    253                    public static final int DTMF_3_VALUE     =  3;
    254                    public static final int DTMF_4_VALUE     =  4;
    255                    public static final int DTMF_5_VALUE     =  5;
    256                    public static final int DTMF_6_VALUE     =  6;
    257                    public static final int DTMF_7_VALUE     =  7;
    258                    public static final int DTMF_8_VALUE     =  8;
    259                    public static final int DTMF_9_VALUE     =  9;
    260                    public static final int DTMF_STAR_VALUE  = 10;
    261                    public static final int DTMF_POUND_VALUE = 11;
    262            }
    263            private final static byte[] P_CONVERSATION_req = {(byte) 90,(byte) 71,(byte) 162,(byte) 7,(byte) 93,(byte) 19};
    264            private final static byte[] P_IDENTITY_req = {(byte) 90,(byte) 71,(byte) 163,(byte) 7,(byte) 93,(byte) 19};
    265            private final static byte[] P_RANK_req = {(byte) 90,(byte) 71,(byte) 164,(byte) 7,(byte) 93,(byte) 19};
    266            private final static byte[] P_REQUESTED_RANK_req = {(byte) 90,(byte) 71,(byte) 165,(byte) 7,(byte) 93,(byte) 19};
    267            private final static byte[] P_TEXT_STATUS_req = {(byte) 90,(byte) 71,(byte) 166,(byte) 7,(byte) 93,(byte) 19};
    268            private final static byte[] P_VOICE_STATUS_req = {(byte) 90,(byte) 71,(byte) 167,(byte) 7,(byte) 93,(byte) 19};
    269            private final static byte[] P_VIDEO_STATUS_req = {(byte) 90,(byte) 71,(byte) 168,(byte) 7,(byte) 93,(byte) 19};
    270            private final static byte[] P_LIVE_IDENTITY_req = {(byte) 90,(byte) 71,(byte) 175,(byte) 7,(byte) 93,(byte) 19};
    271            private final static byte[] P_LIVE_PRICE_FOR_ME_req = {(byte) 90,(byte) 71,(byte) 170,(byte) 7,(byte) 93,(byte) 19};
    272            private final static byte[] P_LIVE_FWD_IDENTITIES_req = {(byte) 90,(byte) 71,(byte) 180,(byte) 7,(byte) 93,(byte) 19};
    273            private final static byte[] P_LIVE_START_TIMESTAMP_req = {(byte) 90,(byte) 71,(byte) 171,(byte) 7,(byte) 93,(byte) 19};
    274            private final static byte[] P_SOUND_LEVEL_req = {(byte) 90,(byte) 71,(byte) 173,(byte) 7,(byte) 93,(byte) 19};
    275            private final static byte[] P_DEBUG_INFO_req = {(byte) 90,(byte) 71,(byte) 174,(byte) 7,(byte) 93,(byte) 19};
    276            private final static byte[] P_LAST_VOICE_ERROR_req = {(byte) 90,(byte) 71,(byte) 179,(byte) 7,(byte) 93,(byte) 19};
    277            private final static byte[] P_QUALITY_PROBLEMS_req = {(byte) 90,(byte) 71,(byte) 181,(byte) 7,(byte) 93,(byte) 19};
    278            private final static byte[] P_LIVE_TYPE_req = {(byte) 90,(byte) 71,(byte) 182,(byte) 7,(byte) 93,(byte) 19};
    279            private final static byte[] P_LIVE_COUNTRY_req = {(byte) 90,(byte) 71,(byte) 183,(byte) 7,(byte) 93,(byte) 19};
    280            private final static byte[] P_TRANSFERRED_BY_req = {(byte) 90,(byte) 71,(byte) 184,(byte) 7,(byte) 93,(byte) 19};
    281            private final static byte[] P_TRANSFERRED_TO_req = {(byte) 90,(byte) 71,(byte) 185,(byte) 7,(byte) 93,(byte) 19};
    282            private final static byte[] P_ADDER_req = {(byte) 90,(byte) 71,(byte) 186,(byte) 7,(byte) 93,(byte) 19};
    283            private final static byte[] P_LAST_LEAVEREASON_req = {(byte) 90,(byte) 71,(byte) 187,(byte) 7,(byte) 93,(byte) 19};
    284            /** Properties of the Participant class */
    285            public enum Property implements PropertyEnumConverting {
    286                    P_UNKNOWN             (0,0,null,0,null),
    287                    P_CONVERSATION        (930, 1, P_CONVERSATION_req, 18, null),
    288                    P_IDENTITY            (931, 2, P_IDENTITY_req, 0, null),
    289                    P_RANK                (932, 3, P_RANK_req, 0, Rank.get(0)),
    290                    P_REQUESTED_RANK      (933, 4, P_REQUESTED_RANK_req, 0, Rank.get(0)),
    291                    P_TEXT_STATUS         (934, 5, P_TEXT_STATUS_req, 0, TextStatus.get(0)),
    292                    P_VOICE_STATUS        (935, 6, P_VOICE_STATUS_req, 0, VoiceStatus.get(0)),
    293                    P_VIDEO_STATUS        (936, 7, P_VIDEO_STATUS_req, 0, VideoStatus.get(0)),
    294                    P_LIVE_IDENTITY       (943, 8, P_LIVE_IDENTITY_req, 0, null),
    295                    P_LIVE_PRICE_FOR_ME   (938, 9, P_LIVE_PRICE_FOR_ME_req, 0, null),
    296                    P_LIVE_FWD_IDENTITIES (948, 10, P_LIVE_FWD_IDENTITIES_req, 0, null),
    297                    P_LIVE_START_TIMESTAMP(939, 11, P_LIVE_START_TIMESTAMP_req, 0, null),
    298                    P_SOUND_LEVEL         (941, 12, P_SOUND_LEVEL_req, 0, null),
    299                    P_DEBUG_INFO          (942, 13, P_DEBUG_INFO_req, 0, null),
    300                    P_LAST_VOICE_ERROR    (947, 14, P_LAST_VOICE_ERROR_req, 0, null),
    301                    P_QUALITY_PROBLEMS    (949, 15, P_QUALITY_PROBLEMS_req, 0, null),
    302                    P_LIVE_TYPE           (950, 16, P_LIVE_TYPE_req, 0, Skype.IdentityType.get(0)),
    303                    P_LIVE_COUNTRY        (951, 17, P_LIVE_COUNTRY_req, 0, null),
    304                    P_TRANSFERRED_BY      (952, 18, P_TRANSFERRED_BY_req, 0, null),
    305                    P_TRANSFERRED_TO      (953, 19, P_TRANSFERRED_TO_req, 0, null),
    306                    P_ADDER               (954, 20, P_ADDER_req, 0, null),
    307                    P_LAST_LEAVEREASON    (955, 21, P_LAST_LEAVEREASON_req, 0, Skype.LeaveReason.get(0));
    308                    private final int    key;
    309                    private final int    idx;
    310                    private final byte[] req;
    311                    private final int    mod;
    312                    private final EnumConverting enumConverter;
    313                    Property(int key, int idx, byte[] req, int mod, EnumConverting converter) {
    314                            this.key = key;
    315                            this.idx = idx;
    316                            this.req = req;
    317                            this.mod = mod;
    318                            this.enumConverter = converter;
    319                    };
    320                    public boolean  isCached()    { return idx > 0;   }
    321                    public int      getIdx()      { return idx;       }
    322                    public int      getId()       { return key;       }
    323                    public byte[]   getRequest()  { return req;       }
    324                    public EnumConverting getDefault()  { return P_UNKNOWN; }
    325                    public int      getModuleId() { return mod;       }
    326                    public EnumConverting getEnumConverter()    { return enumConverter;   }
    327                    public EnumConverting convert(final int from) { return Property.get(from); }
    328                    public EnumConverting[] getArray(final int size) { return new Property[size]; }
    329                    public static Property get(final int from) {
    330                            switch (from) {
    331                            case 930: return P_CONVERSATION;
    332                            case 931: return P_IDENTITY;
    333                            case 932: return P_RANK;
    334                            case 933: return P_REQUESTED_RANK;
    335                            case 934: return P_TEXT_STATUS;
    336                            case 935: return P_VOICE_STATUS;
    337                            case 936: return P_VIDEO_STATUS;
    338                            case 943: return P_LIVE_IDENTITY;
    339                            case 938: return P_LIVE_PRICE_FOR_ME;
    340                            case 948: return P_LIVE_FWD_IDENTITIES;
    341                            case 939: return P_LIVE_START_TIMESTAMP;                        case 941: return P_SOUND_LEVEL;
    342                            case 942: return P_DEBUG_INFO;
    343                            case 947: return P_LAST_VOICE_ERROR;
    344                            case 949: return P_QUALITY_PROBLEMS;
    345                            case 950: return P_LIVE_TYPE;
    346                            case 951: return P_LIVE_COUNTRY;
    347                            case 952: return P_TRANSFERRED_BY;
    348                            case 953: return P_TRANSFERRED_TO;
    349                            case 954: return P_ADDER;
    350                            case 955: return P_LAST_LEAVEREASON;
    351                            }
    352                            return P_UNKNOWN;
    353                    }
    354                    public static final int P_CONVERSATION_VALUE         = 930;
    355                    public static final int P_IDENTITY_VALUE             = 931;
    356                    public static final int P_RANK_VALUE                 = 932;
    357                    public static final int P_REQUESTED_RANK_VALUE       = 933;
    358                    public static final int P_TEXT_STATUS_VALUE          = 934;
    359                    public static final int P_VOICE_STATUS_VALUE         = 935;
    360                    public static final int P_VIDEO_STATUS_VALUE         = 936;
    361                    public static final int P_LIVE_IDENTITY_VALUE        = 943;
    362                    public static final int P_LIVE_PRICE_FOR_ME_VALUE    = 938;
    363                    public static final int P_LIVE_FWD_IDENTITIES_VALUE  = 948;
    364                    public static final int P_LIVE_START_TIMESTAMP_VALUE = 939;
    365                    public static final int P_SOUND_LEVEL_VALUE          = 941;
    366                    public static final int P_DEBUG_INFO_VALUE           = 942;
    367                    public static final int P_LAST_VOICE_ERROR_VALUE     = 947;
    368                    public static final int P_QUALITY_PROBLEMS_VALUE     = 949;
    369                    public static final int P_LIVE_TYPE_VALUE            = 950;
    370                    public static final int P_LIVE_COUNTRY_VALUE         = 951;
    371                    public static final int P_TRANSFERRED_BY_VALUE       = 952;
    372                    public static final int P_TRANSFERRED_TO_VALUE       = 953;
    373                    public static final int P_ADDER_VALUE                = 954;
    374                    public static final int P_LAST_LEAVEREASON_VALUE     = 955;
    375                    public static final Property[] mget_info_mreq = { P_VOICE_STATUS, P_TEXT_STATUS, P_VIDEO_STATUS, P_RANK, P_LIVE_TYPE, P_LIVE_PRICE_FOR_ME, P_IDENTITY };
    376            }
    377            private final static byte[] canSetRankTo_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 1};
    378            /** Checks whether the current user can set this Participant's conversation privileges to the specified RANK. This enables you to gray out or disable in your UI all the unavailable options for Participant.SetRankTo method.  * @param rank Participant.RANK value to check for.
    379             * @return result Returns true if local user can set participant's rank to the value given in rank argument.
    380             */
    381            public boolean canSetRankTo(Rank rank) {
    382                    try {
    383                            return sidDoRequest(canSetRankTo_req)
    384                            .addEnumParm(1, rank)
    385                            .endRequest().getBoolParm(1, true);
    386                    } catch(IOException e) {
    387                            mSidRoot.sidOnFatalError(e);
    388                            return false
    389                    ;}
    390            }
    391            private final static byte[] setRankTo_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 2};
    392            /** Sets Participant's conversation privileges to the given RANK  * @param rank Target Participant.RANK value.
    393             */
    394            public void setRankTo(Rank rank) {
    395                    try {
    396                            sidDoRequest(setRankTo_req)
    397                            .addEnumParm(1, rank)
    398                            .endOneWay();
    399                    } catch(IOException e) {
    400                            mSidRoot.sidOnFatalError(e);
    401                    }
    402            }
    403            private final static byte[] ring_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 3};
    404            /** Initiates live conversation attempt with Participant.  * @param identityToUse Ring an alternate identity, such as a PSTN number.
    405             * @param videoCall Enable video.
    406             * @param nrofRedials Unused.
    407             * @param redialPeriod Unused.
    408             * @param autoStartVm Unused. On dialog, if falling on VM, greeting and recording will be automatically started.
    409             * @param origin When call is initiated from web link, must contain the URI that was used
    410             */
    411            public void ring(String identityToUse, boolean videoCall, int nrofRedials, int redialPeriod, boolean autoStartVm, String origin) {
    412                    try {
    413                            sidDoRequest(ring_req)
    414                            .addStringParm(1, identityToUse)
    415                            .addBoolParm(2, videoCall)
    416                            .addUintParm(3, nrofRedials)
    417                            .addUintParm(4, redialPeriod)
    418                            .addBoolParm(5, autoStartVm)
    419                            .addStringParm(6, origin)
    420                            .endOneWay();
    421                    } catch(IOException e) {
    422                            mSidRoot.sidOnFatalError(e);
    423                    }
    424            }
    425            private final static byte[] ringIt_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 8};
    426            /** Rings this participant, using P_LIVE_IDENTITY property if set.  */
    427            public void ringIt() {
    428                    try {
    429                            sidDoRequest(ringIt_req)
    430                            .endOneWay();
    431                    } catch(IOException e) {
    432                            mSidRoot.sidOnFatalError(e);
    433                    }
    434            }
    435            private final static byte[] setLiveIdentityToUse_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 7};
    436            /** Sets LIVE_IDENTITY property, an alternate identity to use when ringing, such as a PSTN.  * @param identityToUse Empty string will reset it to default, i.e IDENTITY property value
    437             */
    438            public void setLiveIdentityToUse(String identityToUse) {
    439                    try {
    440                            sidDoRequest(setLiveIdentityToUse_req)
    441                            .addStringParm(1, identityToUse)
    442                            .endOneWay();
    443                    } catch(IOException e) {
    444                            mSidRoot.sidOnFatalError(e);
    445                    }
    446            }
    447            private final static byte[] getVideo_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 4};
    448            /**
    449             * Retrieves a reference to the Video object that corresponds to the Participant. It can be either local video - you can check if this participant's name (P_IDENTITY property) matches the name of the currently logged in account (P_SKYPENAME property) or incoming video from a remote participant.
    450             *
    451             * Note that for GetVideo to be successful, the video has to be available for that participant. This can be checked for by examining Participant VIDEO_STATUS property - once it becomes VIDEO_AVAILABLE - you can use GetVideo to obtain the Video object.
    452             * @return video Returns reference to a constructed video object.
    453             */
    454            public Video getVideo() {
    455                    try {
    456                            return (Video) sidDoRequest(getVideo_req)
    457                            .endRequest().getObjectParm(1, 11, true);
    458                    } catch(IOException e) {
    459                            mSidRoot.sidOnFatalError(e);
    460                            return null
    461                    ;}
    462            }
    463            private final static byte[] hangup_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 5};
    464            /** Removes this participant from the current live session. Note that this does not remove the participant from conversation (for this, use Participant.Retire). It only removes participant from live state.  */
    465            public void hangup() {
    466                    try {
    467                            sidDoRequest(hangup_req)
    468                            .endOneWay();
    469                    } catch(IOException e) {
    470                            mSidRoot.sidOnFatalError(e);
    471                    }
    472            }
    473            private final static byte[] retire_req = {(byte) 90,(byte) 82,(byte) 19,(byte) 6};
    474            /** Forcibly removes this participant from conversation. This method is for removing other people from conversations (for example: as administrative punishment for flooding conversation with spam messages). For local user to leave a conversation, use Conversation.RetireFrom instead.  */
    475            public void retire() {
    476                    try {
    477                            sidDoRequest(retire_req)
    478                            .endOneWay();
    479                    } catch(IOException e) {
    480                            mSidRoot.sidOnFatalError(e);
    481                    }
    482            }
    483            /***
    484             * generic multiget of a list of Property
    485             * @param requested the list of requested properties of Participant
    486             * @return SidGetResponding
    487             */
    488            public SidGetResponding sidMultiGet(Property[] requested) {
    489                    return super.sidMultiGet(requested);
    490            }
    491            /***
    492             * generic multiget of list of Property for a list of Participant
    493             * @param requested the list of requested properties
    494             * @return SidGetResponding[] can be casted to (Participant[]) if all properties are cached
    495             */
    496            static public SidGetResponding[] sidMultiGet(Property[] requested, Participant[] objects) {
    497                    return SidObject.sidMultiGet(requested, objects);
    498            }
    499            /*** multiget the following properties
    500             * - P_VOICE_STATUS
    501             * - P_TEXT_STATUS
    502             * - P_VIDEO_STATUS
    503             * - P_RANK
    504             * - P_LIVE_TYPE
    505             * - P_LIVE_PRICE_FOR_ME
    506             * - P_IDENTITY
    507             */
    508            public Participant mgetInfo() {
    509                    return (Participant) super.sidMultiGet(Property.mget_info_mreq, this);
    510            }
    511            /*** multiget the following properties for a list of Participant
    512             * - P_VOICE_STATUS
    513             * - P_TEXT_STATUS
    514             * - P_VIDEO_STATUS
    515             * - P_RANK
    516             * - P_LIVE_TYPE
    517             * - P_LIVE_PRICE_FOR_ME
    518             * - P_IDENTITY
    519             * @param objects targets of the request
    520             * @return Participant[] responses
    521             */
    522            static public Participant[] mgetInfo(Participant[] objects) {
    523                    return (Participant[]) SidObject.sidMultiGet(Property.mget_info_mreq, objects, objects);
    524            }
    525            /** [ALL] ID of corresponding conversation. Here and below, [ALL] tag indicates that the property has meaning and is set in context of all participants in the conversation. [OTHERS] tag has meaning only for participants who are not the local user.  */     public Conversation getConversation() {
    526                    synchronized(this) {
    527                            if ((mSidCached & 0x1) != 0)
    528                                    return mConversation;
    529                    }
    530                    return (Conversation) sidRequestObjectProperty(Property.P_CONVERSATION);
    531            }
    532            /** [ALL] skypename OR pstn_number OR namespace:identity */
    533            public String getIdentity() {
    534                    synchronized(this) {
    535                            if ((mSidCached & 0x2) != 0)
    536                                    return mIdentity;
    537                    }
    538                    return sidRequestStringProperty(Property.P_IDENTITY);
    539            }
    540            /** [ALL] Participant.RANK */
    541            public Rank getRank() {
    542                    synchronized(this) {
    543                            if ((mSidCached & 0x4) != 0)
    544                                    return mRank;
    545                    }
    546                    return (Rank) sidRequestEnumProperty(Property.P_RANK);
    547            }
    548            /** Not set (should be: requested Participant.RANK, higher than the current one) */
    549            public Rank getRequestedRank() {
    550                    synchronized(this) {
    551                            if ((mSidCached & 0x8) != 0)
    552                                    return mRequestedRank;
    553                    }
    554                    return (Rank) sidRequestEnumProperty(Property.P_REQUESTED_RANK);
    555            }
    556            /** [ALL] the typing indicator */
    557            public TextStatus getTextStatus() {
    558                    synchronized(this) {
    559                            if ((mSidCached & 0x10) != 0)
    560                                    return mTextStatus;
    561                    }
    562                    return (TextStatus) sidRequestEnumProperty(Property.P_TEXT_STATUS);
    563            }
    564            /** [ALL] voice status */
    565            public VoiceStatus getVoiceStatus() {
    566                    synchronized(this) {
    567                            if ((mSidCached & 0x20) != 0)
    568                                    return mVoiceStatus;
    569                    }
    570                    return (VoiceStatus) sidRequestEnumProperty(Property.P_VOICE_STATUS);
    571            }
    572            /** [ALL] video status */
    573            public VideoStatus getVideoStatus() {
    574                    synchronized(this) {
    575                            if ((mSidCached & 0x40) != 0)
    576                                    return mVideoStatus;
    577                    }
    578                    return (VideoStatus) sidRequestEnumProperty(Property.P_VIDEO_STATUS);
    579            }
    580            /** [ALL] identity that was used to establish current live session with that participant (can be different from participant identity) */
    581            public String getLiveIdentity() {
    582                    synchronized(this) {
    583                            if ((mSidCached & 0x80) != 0)
    584                                    return mLiveIdentity;
    585                    }
    586                    return sidRequestStringProperty(Property.P_LIVE_IDENTITY);
    587            }
    588            /** [OTHERS] 'price_per_minute_float currency' - eg '0.01 EUR'. Note that this property obtains value only after the participant goes into live state. It cannot be used to display call rates before the call starts.  */
    589            public String getLivePriceForMe() {
    590                    synchronized(this) {
    591                            if ((mSidCached & 0x100) != 0)
    592                                    return mLivePriceForMe;
    593                    }
    594                    return sidRequestStringProperty(Property.P_LIVE_PRICE_FOR_ME);
    595            }
    596            /** [OTHERS] list of identities where the live session is being forwarded (if they are disclosed), space separated */
    597            public String getLiveFwdIdentities() {
    598                    synchronized(this) {
    599                            if ((mSidCached & 0x200) != 0)
    600                                    return mLiveFwdIdentities;
    601                    }
    602                    return sidRequestStringProperty(Property.P_LIVE_FWD_IDENTITIES);
    603            }
    604            /** [ALL] time of joining the live session */
    605            public int getLiveStartTimestamp() {
    606                    synchronized(this) {
    607                            if ((mSidCached & 0x400) != 0)
    608                                    return mLiveStartTimestamp;
    609                    }
    610                    return sidRequestUintProperty(Property.P_LIVE_START_TIMESTAMP);
    611            }
    612            /** [ALL] current 'loudness' level when SPEAKING (0..10) */
    613            public int getSoundLevel() {
    614                    synchronized(this) {
    615                            if ((mSidCached & 0x800) != 0)
    616                                    return mSoundLevel;
    617                    }
    618                    return sidRequestUintProperty(Property.P_SOUND_LEVEL);
    619            }
    620            /** [OTHERS] call (audio and video) debug info */
    621            public String getDebugInfo() {
    622                    synchronized(this) {
    623                            if ((mSidCached & 0x1000) != 0)
    624                                    return mDebugInfo;
    625                    }
    626                    return sidRequestStringProperty(Property.P_DEBUG_INFO);
    627            }
    628            /** [OTHERS] DEPRECATED, use last_leavereason instead */
    629            public String getLastVoiceError() {
    630                    synchronized(this) {
    631                            if ((mSidCached & 0x2000) != 0)
    632                                    return mLastVoiceError;
    633                    }
    634                    return sidRequestStringProperty(Property.P_LAST_VOICE_ERROR);
    635            }
    636            /** [ALL] space separated tokens values: CPU_INUSE CPU_SLOW CPU_HIGH HIGH_ECHO HIGH_NOISE MUTED_INPUT LOW_INPUT MUTED_INPUT_ACTIVITY FW_STRONG FW_BAD NOT_UDP CALL_BW_LOW RECORD_ERROR + values in video debug info */
    637            public String getQualityProblems() {
    638                    synchronized(this) {
    639                            if ((mSidCached & 0x4000) != 0)
    640                                    return mQualityProblems;
    641                    }
    642                    return sidRequestStringProperty(Property.P_QUALITY_PROBLEMS);
    643            }
    644            /** [ALL] participant type during livesession as specified in IDENTITYTYPE */
    645            public Skype.IdentityType getLiveType() {
    646                    synchronized(this) {
    647                            if ((mSidCached & 0x8000) != 0)
    648                                    return mLiveType;
    649                    }
    650                    return (Skype.IdentityType) sidRequestEnumProperty(Property.P_LIVE_TYPE);
    651            }
    652            /** [OTHERS] participant livesession country code - used for emergency calls only atm */
    653            public String getLiveCountry() {
    654                    synchronized(this) {
    655                            if ((mSidCached & 0x10000) != 0)
    656                                    return mLiveCountry;
    657                    }
    658                    return sidRequestStringProperty(Property.P_LIVE_COUNTRY);
    659            }
    660            /** [OTHERS] Transferor identity (transferee side)  */
    661            public String getTransferredBy() {
    662                    synchronized(this) {
    663                            if ((mSidCached & 0x20000) != 0)
    664                                    return mTransferredBy;
    665                    }
    666                    return sidRequestStringProperty(Property.P_TRANSFERRED_BY);
    667            }
    668            /** [OTHERS] Identity of recipient of transfer (transferor side, caller side)  */
    669            public String getTransferredTo() {
    670                    synchronized(this) {
    671                            if ((mSidCached & 0x40000) != 0)
    672                                    return mTransferredTo;
    673                    }
    674                    return sidRequestStringProperty(Property.P_TRANSFERRED_TO);
    675            }
    676            /** [ALL] Identity of the user who added this participant to the conversation, type: Sid.String   */
    677            public String getAdder() {
    678                    synchronized(this) {
    679                            if ((mSidCached & 0x80000) != 0)
    680                                    return mAdder;
    681                    }
    682                    return sidRequestStringProperty(Property.P_ADDER);
    683            }
    684            /** [OTHERS] last reason for leaving conversation or live session.                   provides an enum alternative to last_voice_error as well                   as the reason this participant RETIRED, if so */
    685            public Skype.LeaveReason getLastLeavereason() {
    686                    synchronized(this) {
    687                            if ((mSidCached & 0x100000) != 0)
    688                                    return mLastLeavereason;
    689                    }
    690                    return (Skype.LeaveReason) sidRequestEnumProperty(Property.P_LAST_LEAVEREASON);
    691            }
    692            public String sidGetStringProperty(final PropertyEnumConverting prop) {
    693                    switch(prop.getId()) {
    694                    case 931:
    695                            return mIdentity;
    696                    case 943:
    697                            return mLiveIdentity;
    698                    case 938:
    699                            return mLivePriceForMe;
    700                    case 948:
    701                            return mLiveFwdIdentities;
    702                    case 942:
    703                            return mDebugInfo;
    704                    case 947:
    705                            return mLastVoiceError;
    706                    case 949:
    707                            return mQualityProblems;
    708                    case 951:
    709                            return mLiveCountry;
    710                    case 952:
    711                            return mTransferredBy;
    712                    case 953:
    713                            return mTransferredTo;
    714                    case 954:
    715                            return mAdder;
    716                    }
    717                    return "";
    718            }
    719            public SidObject sidGetObjectProperty(final PropertyEnumConverting prop) {
    720                    assert(prop.getId() == 930);
    721                    return mConversation;
    722            }
    723            public int sidGetIntProperty(final PropertyEnumConverting prop) {
    724                    switch(prop.getId()) {
    725                    case 939:
    726                            return mLiveStartTimestamp;
    727                    case 941:
    728                            return mSoundLevel;
    729                    }
    730                    return 0;
    731            }
    732            public EnumConverting sidGetEnumProperty(final PropertyEnumConverting prop) {
    733                    switch(prop.getId()) {
    734                    case 932:
    735                            return mRank;
    736                    case 933:
    737                            return mRequestedRank;
    738                    case 934:
    739                            return mTextStatus;
    740                    case 935:
    741                            return mVoiceStatus;
    742                    case 936:
    743                            return mVideoStatus;
    744                    case 950:
    745                            return mLiveType;
    746                    case 955:
    747                            return mLastLeavereason;
    748                    }
    749                    return null;
    750            }
    751            public String getPropertyAsString(final int prop) {
    752                    switch (prop) {
    753                    case 930: return Integer.toString(getConversation().getOid());
    754                    case 931: return getIdentity();
    755                    case 932: return getRank().toString();
    756                    case 933: return getRequestedRank().toString();
    757                    case 934: return getTextStatus().toString();
    758                    case 935: return getVoiceStatus().toString();
    759                    case 936: return getVideoStatus().toString();
    760                    case 943: return getLiveIdentity();
    761                    case 938: return getLivePriceForMe();
    762                    case 948: return getLiveFwdIdentities();
    763                    case 939: return Integer.toString(getLiveStartTimestamp());
    764                    case 941: return Integer.toString(getSoundLevel());
    765                    case 942: return getDebugInfo();
    766                    case 947: return getLastVoiceError();
    767                    case 949: return getQualityProblems();
    768                    case 950: return getLiveType().toString();
    769                    case 951: return getLiveCountry();
    770                    case 952: return getTransferredBy();
    771                    case 953: return getTransferredTo();
    772                    case 954: return getAdder();
    773                    case 955: return getLastLeavereason().toString();
    774                    }
    775                    return "<unkown>";
    776            }
    777            public String getPropertyAsString(final Property prop) {
    778                    return getPropertyAsString(prop.getId());
    779            }
    780            protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue) {               final Property property = Property.get(propertyId);
    781                    if (property == Property.P_UNKNOWN) return;
    782                    final int idx = property.getIdx();
    783                    if (idx != 0) {
    784                            int bit  = 1<<((idx-1)%32);
    785                            synchronized (this) {
    786                                    mSidCached|=bit;
    787                                    switch (propertyId) {
    788                                    case 931:
    789                                            if (svalue != null) mIdentity = svalue;
    790                                            else mSidCached &=~bit;
    791                                            break;
    792                                    case 932: mRank = Rank.get(value); break;
    793                                    case 933: mRequestedRank = Rank.get(value); break;
    794                                    case 934: mTextStatus = TextStatus.get(value); break;
    795                                    case 935: mVoiceStatus = VoiceStatus.get(value); break;
    796                                    case 936: mVideoStatus = VideoStatus.get(value); break;
    797                                    case 943:
    798                                            if (svalue != null) mLiveIdentity = svalue;
    799                                            else mSidCached &=~bit;
    800                                            break;
    801                                    case 938:
    802                                            if (svalue != null) mLivePriceForMe = svalue;
    803                                            else mSidCached &=~bit;
    804                                            break;
    805                                    case 948:
    806                                            if (svalue != null) mLiveFwdIdentities = svalue;
    807                                            else mSidCached &=~bit;
    808                                            break;
    809                                    case 939: mLiveStartTimestamp = value; break;
    810                                    case 941: mSoundLevel = value; break;
    811                                    case 942:
    812                                            if (svalue != null) mDebugInfo = svalue;
    813                                            else mSidCached &=~bit;
    814                                            break;
    815                                    case 947:
    816                                            if (svalue != null) mLastVoiceError = svalue;
    817                                            else mSidCached &=~bit;
    818                                            break;
    819                                    case 949:
    820                                            if (svalue != null) mQualityProblems = svalue;
    821                                            else mSidCached &=~bit;
    822                                            break;
    823                                    case 950: mLiveType = Skype.IdentityType.get(value); break;
    824                                    case 951:
    825                                            if (svalue != null) mLiveCountry = svalue;
    826                                            else mSidCached &=~bit;
    827                                            break;
    828                                    case 952:
    829                                            if (svalue != null) mTransferredBy = svalue;
    830                                            else mSidCached &=~bit;
    831                                            break;
    832                                    case 953:
    833                                            if (svalue != null) mTransferredTo = svalue;
    834                                            else mSidCached &=~bit;
    835                                            break;
    836                                    case 954:
    837                                            if (svalue != null) mAdder = svalue;
    838                                            else mSidCached &=~bit;
    839                                            break;
    840                                    case 955: mLastLeavereason = Skype.LeaveReason.get(value); break;
    841                                    default: mSidCached|=bit; break;
    842                                    }
    843                            }
    844                    }
    845                    ParticipantListener listener = ((Skype) mSidRoot).getParticipantListener();
    846                    if (listener != null)
    847                            listener.onPropertyChange(this, property, value, svalue);
    848            }
    849            public void sidSetProperty(final PropertyEnumConverting prop, final String newValue) {
    850                    final int propId = prop.getId();
    851                    switch(propId) {
    852                    case 931:
    853                            mSidCached |= 0x2;
    854                            mIdentity=  newValue;
    855                            break;
    856                    case 943:
    857                            mSidCached |= 0x80;
    858                            mLiveIdentity=  newValue;
    859                            break;
    860                    case 938:
    861                            mSidCached |= 0x100;
    862                            mLivePriceForMe=  newValue;
    863                            break;
    864                    case 948:
    865                            mSidCached |= 0x200;
    866                            mLiveFwdIdentities=  newValue;
    867                            break;
    868                    case 942:
    869                            mSidCached |= 0x1000;
    870                            mDebugInfo=  newValue;
    871                            break;
    872                    case 947:
    873                            mSidCached |= 0x2000;
    874                            mLastVoiceError=  newValue;
    875                            break;
    876                    case 949:
    877                            mSidCached |= 0x4000;
    878                            mQualityProblems=  newValue;
    879                            break;
    880                    case 951:
    881                            mSidCached |= 0x10000;
    882                            mLiveCountry=  newValue;
    883                            break;
    884                    case 952:
    885                            mSidCached |= 0x20000;
    886                            mTransferredBy=  newValue;
    887                            break;
    888                    case 953:
    889                            mSidCached |= 0x40000;
    890                            mTransferredTo=  newValue;
    891                            break;
    892                    case 954:
    893                            mSidCached |= 0x80000;
    894                            mAdder=  newValue;
    895                            break;
    896                    }
    897            }
    898            public void sidSetProperty(final PropertyEnumConverting prop, final SidObject newValue) {
    899                    final int propId = prop.getId();
    900                    assert(propId == 930);
    901                    mSidCached |= 0x1;
    902                    mConversation= (Conversation) newValue;
    903            }
    904            public void sidSetProperty(final PropertyEnumConverting prop, final int newValue) {
    905                    final int propId = prop.getId();
    906                    switch(propId) {
    907                    case 932:
    908                            mSidCached |= 0x4;
    909                            mRank= Rank.get(newValue);
    910                            break;
    911                    case 933:
    912                            mSidCached |= 0x8;
    913                            mRequestedRank= Rank.get(newValue);
    914                            break;
    915                    case 934:
    916                            mSidCached |= 0x10;
    917                            mTextStatus= TextStatus.get(newValue);
    918                            break;
    919                    case 935:
    920                            mSidCached |= 0x20;
    921                            mVoiceStatus= VoiceStatus.get(newValue);
    922                            break;
    923                    case 936:
    924                            mSidCached |= 0x40;
    925                            mVideoStatus= VideoStatus.get(newValue);
    926                            break;
    927                    case 939:
    928                            mSidCached |= 0x400;
    929                            mLiveStartTimestamp=  newValue;
    930                            break;
    931                    case 941:
    932                            mSidCached |= 0x800;
    933                            mSoundLevel=  newValue;
    934                            break;
    935                    case 950:
    936                            mSidCached |= 0x8000;
    937                            mLiveType= Skype.IdentityType.get(newValue);
    938                            break;
    939                    case 955:
    940                            mSidCached |= 0x100000;
    941                            mLastLeavereason= Skype.LeaveReason.get(newValue);
    942                            break;
    943                    }
    944            }
    945            public Conversation       mConversation;
    946            public String             mIdentity;
    947            public Rank               mRank;
    948            public Rank               mRequestedRank;
    949            public TextStatus         mTextStatus;
    950            public VoiceStatus        mVoiceStatus;
    951            public VideoStatus        mVideoStatus;
    952            public String             mLiveIdentity;
    953            public String             mLivePriceForMe;
    954            public String             mLiveFwdIdentities;
    955            public int                mLiveStartTimestamp;
    956            public int                mSoundLevel;
    957            public String             mDebugInfo;
    958            public String             mLastVoiceError;
    959            public String             mQualityProblems;
    960            public Skype.IdentityType mLiveType;
    961            public String             mLiveCountry;
    962            public String             mTransferredBy;
    963            public String             mTransferredTo;
    964            public String             mAdder;
    965            public Skype.LeaveReason  mLastLeavereason;
    966            public int moduleId() {
    967                    return 19;
    968            }
    969
    970            public Participant(final int oid, final SidRoot root) {
    971                    super(oid, root, 21);
    972            }
    973    }