    001    package com.skype.api;
    002
    003    import com.skype.ipc.SidRoot;
    004    import com.skype.ipc.SidObject;
    005    import com.skype.ipc.EnumConverting;
    006    import com.skype.ipc.PropertyEnumConverting;
    007    import com.skype.ipc.Decoding;
    008    import com.skype.ipc.Encoding;
    009    import com.skype.ipc.Encoding;
    010    import java.io.IOException;
    011    import com.skype.ipc.PropertyEnumConverting;
    012    import com.skype.ipc.SidGetResponding;
    013
    014    /**
    015     * Wrapper class that includes SMS-specific properties and methods, such as P_BODY and GetTargetPrice. Instantiate SMS instances using Skype.CreateOutgoingSms; post SMS messages to a Conversation using Conversation.PostSMS.
    016     *
    017     * Each SMS can have multiple targets (normalized phone numbers). Note that in a Conversation context, every SMS instance has a corresponding Message instance. Once you've posted an SMS, you can retrieve its corresponding Message instance using Sms.GetPropChatmsgId. That Message instance's P_BODY_XML property contains the SMS message data, such as price, target phone number(s), failure codes, and so forth, which you can parsed out and display in the UI. To put it another way, the object chain goes like this:
    018     *
    019     * @code
    020     * Conversation->Message->SMS
    021     * </CODE>
    022     *
    023     * Note that SkypeKit SDK supports outgoing SMS messages only. SkypeKit clients, even when logged in with accounts that have SkypeIn numbers, cannot receive SMS messages.
    024     */
    025    public final class Sms extends SidObject {
    026            public enum Type implements EnumConverting {
    027                    /** a normal outgoing SMS message */
    028                    OUTGOING                 (2),
    029                    /** a message requesting a SMS confirmation code be sent to the number provided */
    030                    CONFIRMATION_CODE_REQUEST(3),
    031                    /** a message returning the SMS confirmation code received as a result of a CONFIRMATION_CODE_REQUEST to authorize it */
    032                    CONFIRMATION_CODE_SUBMIT (4);
    033                    private final int key;
    034                    Type(int key) {
    035                            this.key = key;
    036                    };
    037                    public int getId()   { return key; }
    038                    public EnumConverting getDefault() { return OUTGOING; }
    039                    public EnumConverting convert(int from) { return Type.get(from); }
    040                    public EnumConverting[] getArray(final int size) { return new Type[size]; }
    041                    public static Type get(int from) {
    042                            switch (from) {
    043                            case 2: return OUTGOING;
    044                            case 3: return CONFIRMATION_CODE_REQUEST;
    045                            case 4: return CONFIRMATION_CODE_SUBMIT;
    046                            }
    047                            return OUTGOING;
    048                    }
    049                    public static final int OUTGOING_VALUE                  = 2;
    050                    public static final int CONFIRMATION_CODE_REQUEST_VALUE = 3;
    051                    public static final int CONFIRMATION_CODE_SUBMIT_VALUE  = 4;
    052            }
    053            public enum Status implements EnumConverting {
    054                    COMPOSING          (3),
    055                    SENDING_TO_SERVER  (4),
    056                    SENT_TO_SERVER     (5),
    057                    DELIVERED          (6),
    058                    SOME_TARGETS_FAILED(7),
    059                    FAILED             (8);
    060                    private final int key;
    061                    Status(int key) {
    062                            this.key = key;
    063                    };
    064                    public int getId()   { return key; }
    065                    public EnumConverting getDefault() { return COMPOSING; }
    066                    public EnumConverting convert(int from) { return Status.get(from); }
    067                    public EnumConverting[] getArray(final int size) { return new Status[size]; }
    068                    public static Status get(int from) {
    069                            switch (from) {
    070                            case 3: return COMPOSING;
    071                            case 4: return SENDING_TO_SERVER;
    072                            case 5: return SENT_TO_SERVER;
    073                            case 6: return DELIVERED;
    074                            case 7: return SOME_TARGETS_FAILED;
    075                            case 8: return FAILED;
    076                            }
    077                            return COMPOSING;
    078                    }
    079                    public static final int COMPOSING_VALUE           = 3;
    080                    public static final int SENDING_TO_SERVER_VALUE   = 4;
    081                    public static final int SENT_TO_SERVER_VALUE      = 5;
    082                    public static final int DELIVERED_VALUE           = 6;
    083                    public static final int SOME_TARGETS_FAILED_VALUE = 7;
    084                    public static final int FAILED_VALUE              = 8;
    085            }
    086            public enum FailureReason implements EnumConverting {
    087                    MISC_ERROR               (1),
    088                    SERVER_CONNECT_FAILED    (2),
    089                    NO_SMS_CAPABILITY        (3),
    090                    INSUFFICIENT_FUNDS       (4),
    091                    INVALID_CONFIRMATION_CODE(5),
    092                    USER_BLOCKED             (6),
    093                    IP_BLOCKED               (7),
    094                    NODE_BLOCKED             (8),
    095                    NO_SENDERID_CAPABILITY   (9);
    096                    private final int key;
    097                    FailureReason(int key) {
    098                            this.key = key;
    099                    };
    100                    public int getId()   { return key; }
    101                    public EnumConverting getDefault() { return MISC_ERROR; }
    102                    public EnumConverting convert(int from) { return FailureReason.get(from); }
    103                    public EnumConverting[] getArray(final int size) { return new FailureReason[size]; }
    104                    public static FailureReason get(int from) {
    105                            switch (from) {
    106                            case 1: return MISC_ERROR;
    107                            case 2: return SERVER_CONNECT_FAILED;
    108                            case 3: return NO_SMS_CAPABILITY;
    109                            case 4: return INSUFFICIENT_FUNDS;
    110                            case 5: return INVALID_CONFIRMATION_CODE;
    111                            case 6: return USER_BLOCKED;
    112                            case 7: return IP_BLOCKED;
    113                            case 8: return NODE_BLOCKED;
    114                            case 9: return NO_SENDERID_CAPABILITY;
    115                            }
    116                            return MISC_ERROR;
    117                    }
    118                    public static final int MISC_ERROR_VALUE                = 1;
    119                    public static final int SERVER_CONNECT_FAILED_VALUE     = 2;
    120                    public static final int NO_SMS_CAPABILITY_VALUE         = 3;
    121                    public static final int INSUFFICIENT_FUNDS_VALUE        = 4;
    122                    public static final int INVALID_CONFIRMATION_CODE_VALUE = 5;
    123                    public static final int USER_BLOCKED_VALUE              = 6;
    124                    public static final int IP_BLOCKED_VALUE                = 7;
    125                    public static final int NODE_BLOCKED_VALUE              = 8;
    126                    public static final int NO_SENDERID_CAPABILITY_VALUE    = 9;
    127            }
    128            public enum TargetStatus implements EnumConverting {
    129                    TARGET_ANALYZING          (1),
    130                    TARGET_UNDEFINED          (2),
    131                    TARGET_ACCEPTABLE         (3),
    132                    TARGET_NOT_ROUTABLE       (4),
    133                    TARGET_DELIVERY_PENDING   (5),
    134                    TARGET_DELIVERY_SUCCESSFUL(6),
    135                    TARGET_DELIVERY_FAILED    (7);
    136                    private final int key;
    137                    TargetStatus(int key) {
    138                            this.key = key;
    139                    };
    140                    public int getId()   { return key; }
    141                    public EnumConverting getDefault() { return TARGET_ANALYZING; }
    142                    public EnumConverting convert(int from) { return TargetStatus.get(from); }
    143                    public EnumConverting[] getArray(final int size) { return new TargetStatus[size]; }
    144                    public static TargetStatus get(int from) {
    145                            switch (from) {
    146                            case 1: return TARGET_ANALYZING;
    147                            case 2: return TARGET_UNDEFINED;
    148                            case 3: return TARGET_ACCEPTABLE;
    149                            case 4: return TARGET_NOT_ROUTABLE;
    150                            case 5: return TARGET_DELIVERY_PENDING;
    151                            case 6: return TARGET_DELIVERY_SUCCESSFUL;
    152                            case 7: return TARGET_DELIVERY_FAILED;
    153                            }
    154                            return TARGET_ANALYZING;
    155                    }
    156                    public static final int TARGET_ANALYZING_VALUE           = 1;
    157                    public static final int TARGET_UNDEFINED_VALUE           = 2;
    158                    public static final int TARGET_ACCEPTABLE_VALUE          = 3;
    159                    public static final int TARGET_NOT_ROUTABLE_VALUE        = 4;
    160                    public static final int TARGET_DELIVERY_PENDING_VALUE    = 5;
    161                    public static final int TARGET_DELIVERY_SUCCESSFUL_VALUE = 6;
    162                    public static final int TARGET_DELIVERY_FAILED_VALUE     = 7;
    163            }
    164            public enum SetBodyResult implements EnumConverting {
    165                    /** body not set. message status wrong or invalid, or body not valid utf8 string */
    166                    BODY_INVALID         (0),
    167                    /** body too long. set, but truncated. charsUntilNextChunk contains maxChunks value */
    168                    BODY_TRUNCATED       (1),
    169                    /** body was set OK */
    170                    BODY_OK              (2),
    171                    /** last unicode char was ignored, as some of the text would be deleted due to conversion */
    172                    BODY_LASTCHAR_IGNORED(3);
    173                    private final int key;
    174                    SetBodyResult(int key) {
    175                            this.key = key;
    176                    };
    177                    public int getId()   { return key; }
    178                    public EnumConverting getDefault() { return BODY_INVALID; }
    179                    public EnumConverting convert(int from) { return SetBodyResult.get(from); }
    180                    public EnumConverting[] getArray(final int size) { return new SetBodyResult[size]; }
    181                    public static SetBodyResult get(int from) {
    182                            switch (from) {
    183                            case 0: return BODY_INVALID;
    184                            case 1: return BODY_TRUNCATED;
    185                            case 2: return BODY_OK;
    186                            case 3: return BODY_LASTCHAR_IGNORED;
    187                            }
    188                            return BODY_INVALID;
    189                    }
    190                    public static final int BODY_INVALID_VALUE          = 0;
    191                    public static final int BODY_TRUNCATED_VALUE        = 1;
    192                    public static final int BODY_OK_VALUE               = 2;
    193                    public static final int BODY_LASTCHAR_IGNORED_VALUE = 3;
    194            }
    195            public enum ConfirmType implements EnumConverting {
    196                    /** Confirm mobile number as SMS sender number */
    197                    ID_SMS    (1),
    198                    /** Confirm mobile number as CLI for SkypeOut calls */
    199                    ID_MOBILE (2),
    200                    /** unused currently */
    201                    ID_SKYPEIN(3);
    202                    private final int key;
    203                    ConfirmType(int key) {
    204                            this.key = key;
    205                    };
    206                    public int getId()   { return key; }
    207                    public EnumConverting getDefault() { return ID_SMS; }
    208                    public EnumConverting convert(int from) { return ConfirmType.get(from); }
    209                    public EnumConverting[] getArray(final int size) { return new ConfirmType[size]; }              public static ConfirmType get(int from) {
    210                            switch (from) {
    211                            case 1: return ID_SMS;
    212                            case 2: return ID_MOBILE;
    213                            case 3: return ID_SKYPEIN;
    214                            }
    215                            return ID_SMS;
    216                    }
    217                    public static final int ID_SMS_VALUE     = 1;
    218                    public static final int ID_MOBILE_VALUE  = 2;
    219                    public static final int ID_SKYPEIN_VALUE = 3;
    220            }
    221            private final static byte[] P_TYPE_req = {(byte) 90,(byte) 71,(byte) 190,(byte) 1,(byte) 93,(byte) 12};
    222            private final static byte[] P_STATUS_req = {(byte) 90,(byte) 71,(byte) 191,(byte) 1,(byte) 93,(byte) 12};
    223            private final static byte[] P_FAILURE_REASON_req = {(byte) 90,(byte) 71,(byte) 192,(byte) 1,(byte) 93,(byte) 12};
    224            private final static byte[] P_IS_FAILED_UNSEEN_req = {(byte) 90,(byte) 71,(byte) 48,(byte) 93,(byte) 12};
    225            private final static byte[] P_TIMESTAMP_req = {(byte) 90,(byte) 71,(byte) 198,(byte) 1,(byte) 93,(byte) 12};
    226            private final static byte[] P_PRICE_req = {(byte) 90,(byte) 71,(byte) 193,(byte) 1,(byte) 93,(byte) 12};
    227            private final static byte[] P_PRICE_PRECISION_req = {(byte) 90,(byte) 71,(byte) 49,(byte) 93,(byte) 12};
    228            private final static byte[] P_PRICE_CURRENCY_req = {(byte) 90,(byte) 71,(byte) 194,(byte) 1,(byte) 93,(byte) 12};
    229            private final static byte[] P_REPLY_TO_NUMBER_req = {(byte) 90,(byte) 71,(byte) 199,(byte) 1,(byte) 93,(byte) 12};
    230            private final static byte[] P_TARGET_NUMBERS_req = {(byte) 90,(byte) 71,(byte) 195,(byte) 1,(byte) 93,(byte) 12};
    231            private final static byte[] P_TARGET_STATUSES_req = {(byte) 90,(byte) 71,(byte) 196,(byte) 1,(byte) 93,(byte) 12};
    232            private final static byte[] P_BODY_req = {(byte) 90,(byte) 71,(byte) 197,(byte) 1,(byte) 93,(byte) 12};
    233            private final static byte[] P_CHAT_MSG_req = {(byte) 90,(byte) 71,(byte) 200,(byte) 6,(byte) 93,(byte) 12};
    234            /** Properties of the Sms class */
    235            public enum Property implements PropertyEnumConverting {
    236                    P_UNKNOWN         (0,0,null,0,null),
    237                    P_TYPE            (190, 1, P_TYPE_req, 0, Type.get(0)),
    238                    P_STATUS          (191, 2, P_STATUS_req, 0, Status.get(0)),
    239                    P_FAILURE_REASON  (192, 3, P_FAILURE_REASON_req, 0, FailureReason.get(0)),
    240                    P_IS_FAILED_UNSEEN(48, 4, P_IS_FAILED_UNSEEN_req, 0, null),
    241                    P_TIMESTAMP       (198, 5, P_TIMESTAMP_req, 0, null),
    242                    P_PRICE           (193, 6, P_PRICE_req, 0, null),
    243                    P_PRICE_PRECISION (49, 7, P_PRICE_PRECISION_req, 0, null),
    244                    P_PRICE_CURRENCY  (194, 8, P_PRICE_CURRENCY_req, 0, null),
    245                    P_REPLY_TO_NUMBER (199, 9, P_REPLY_TO_NUMBER_req, 0, null),
    246                    P_TARGET_NUMBERS  (195, 10, P_TARGET_NUMBERS_req, 0, null),
    247                    P_TARGET_STATUSES (196, 11, P_TARGET_STATUSES_req, 0, null),
    248                    P_BODY            (197, 12, P_BODY_req, 0, null),
    249                    P_CHAT_MSG        (840, 13, P_CHAT_MSG_req, 9, null);
    250                    private final int    key;
    251                    private final int    idx;
    252                    private final byte[] req;
    253                    private final int    mod;
    254                    private final EnumConverting enumConverter;
    255                    Property(int key, int idx, byte[] req, int mod, EnumConverting converter) {
    256                            this.key = key;
    257                            this.idx = idx;
    258                            this.req = req;
    259                            this.mod = mod;
    260                            this.enumConverter = converter;
    261                    };
    262                    public boolean  isCached()    { return idx > 0;   }
    263                    public int      getIdx()      { return idx;       }
    264                    public int      getId()       { return key;       }
    265                    public byte[]   getRequest()  { return req;       }
    266                    public EnumConverting getDefault()  { return P_UNKNOWN; }
    267                    public int      getModuleId() { return mod;       }
    268                    public EnumConverting getEnumConverter()    { return enumConverter;   }
    269                    public EnumConverting convert(final int from) { return Property.get(from); }
    270                    public EnumConverting[] getArray(final int size) { return new Property[size]; }
    271                    public static Property get(final int from) {
    272                            switch (from) {
    273                            case 190: return P_TYPE;
    274                            case 191: return P_STATUS;
    275                            case 192: return P_FAILURE_REASON;
    276                            case  48: return P_IS_FAILED_UNSEEN;
    277                            case 198: return P_TIMESTAMP;
    278                            case 193: return P_PRICE;
    279                            case  49: return P_PRICE_PRECISION;
    280                            case 194: return P_PRICE_CURRENCY;
    281                            case 199: return P_REPLY_TO_NUMBER;
    282                            case 195: return P_TARGET_NUMBERS;
    283                            case 196: return P_TARGET_STATUSES;
    284                            case 197: return P_BODY;
    285                            case 840: return P_CHAT_MSG;
    286                            }
    287                            return P_UNKNOWN;
    288                    }
    289                    public static final int P_TYPE_VALUE             = 190;
    290                    public static final int P_STATUS_VALUE           = 191;
    291                    public static final int P_FAILURE_REASON_VALUE   = 192;
    292                    public static final int P_IS_FAILED_UNSEEN_VALUE =  48;
    293                    public static final int P_TIMESTAMP_VALUE        = 198;
    294                    public static final int P_PRICE_VALUE            = 193;
    295                    public static final int P_PRICE_PRECISION_VALUE  =  49;
    296                    public static final int P_PRICE_CURRENCY_VALUE   = 194;
    297                    public static final int P_REPLY_TO_NUMBER_VALUE  = 199;
    298                    public static final int P_TARGET_NUMBERS_VALUE   = 195;
    299                    public static final int P_TARGET_STATUSES_VALUE  = 196;
    300                    public static final int P_BODY_VALUE             = 197;
    301                    public static final int P_CHAT_MSG_VALUE         = 840;
    302            }
    303            private final static byte[] getTargetStatus_req = {(byte) 90,(byte) 82,(byte) 12,(byte) 4};
    304            /** Retrieves the send status of this SMS to a particular recipient (P_TARGET_STATUSES) either prior to or after invoking Conversation.PostSMS.  * @param target The normalized phone number of the target recipient.
    305             * @return status The send status of the target recipient, for example, TARGET_ANALYZING, TARGET_DELIVERY_PENDING, TARGET_DELIVERY_SUCCESSFUL, TARGET_DELIVERY_FAILED, and so forth. TARGET_UNDEFINED implies that the specified target is not a recipient of this SMS.
    306             */
    307            public TargetStatus getTargetStatus(String target) {
    308                    try {
    309                            return (TargetStatus) sidDoRequest(getTargetStatus_req)
    310                            .addStringParm(1, target)
    311                            .endRequest().getEnumParm(1, TargetStatus.get(0), true);
    312                    } catch(IOException e) {
    313                            mSidRoot.sidOnFatalError(e);
    314                            return TargetStatus.get(0)
    315                    ;}
    316            }
    317            private final static byte[] getTargetPrice_req = {(byte) 90,(byte) 82,(byte) 12,(byte) 13};
    318            /**
    319             * Retrieves the amount of Skype credit necessary to send the SMS to a particular recipient. Defaults to -1 on instantiation and set only when that recipient's status reflects TARGET_ACCEPTABLE. Use Sms.GetPropPrice to retrieve the total cost of this SMS.
    320             *
    321             * Note that the target price is an integer value. Calculate the actual price (in units specified by P_PRICE_CURRENCY) using P_PRICE_PRECISION as:
    322             * @code
    323             * actualTargetPrice = targetPrice / 10^pricePrecision;
    324             * </CODE>
    325             * @param target The normalized phone number of the target recipient.
    326             * @return price The price of sending this SMS message to the target recipient.
    327             */
    328            public int getTargetPrice(String target) {
    329                    try {
    330                            return sidDoRequest(getTargetPrice_req)
    331                            .addStringParm(1, target)
    332                            .endRequest().getUintParm(1, true);
    333                    } catch(IOException e) {
    334                            mSidRoot.sidOnFatalError(e);
    335                            return 0
    336                    ;}
    337            }
    338            private final static byte[] setTargets_req = {(byte) 90,(byte) 82,(byte) 12,(byte) 6};
    339            /** Sets the recipient(s) of this SMS. Note that each invocation replaces the target list and re-calculates all prices - they are not additive!  * @param numbers Normalized phone number(s) of the intended recipient(s).
    340             * @return success Set to true if the target list appears to contain valid, normalized telephone numbers. Note that this check is not very reliable. Actual target validity checking occurs asynchronously in the background, and manifests itself as a series of Sms.P_TARGET_STATUSES property change events.
    341             */
    342            public boolean setTargets(String[] numbers) {
    343                    try {
    344                            return sidDoRequest(setTargets_req)
    345                            .addStringListParm(1, numbers)
    346                            .endRequest().getBoolParm(1, true);
    347                    } catch(IOException e) {
    348                            mSidRoot.sidOnFatalError(e);
    349                            return false
    350                    ;}
    351            }
    352            private final static byte[] setBody_req = {(byte) 90,(byte) 82,(byte) 12,(byte) 7};
    353            public class SetBodyResponse {
    354                    public SetBodyResult result;
    355                    public String[] chunks;
    356                    public int charsUntilNextChunk;
    357            };
    358
    359            /** -The- method for setting the body text of this SMS. While Conversation.PostSMS does have a body argument, that argument is currently unused.  * @param text Message body text.
    360             * @return SetBodyResponse
    361             * <br> - result Whether the Message body was successfully set and if not, why not.
    362             * <br> - chunks The Message body as a list of individual chunks.          * <br> - charsUntilNextChunk Number of available characters until creation of the next chunk becomes necessary.
    363             */
    364            public SetBodyResponse setBody(String text) {
    365                    try {
    366                            Decoding decoder = sidDoRequest(setBody_req)
    367                            .addStringParm(1, text)
    368                            .endRequest();
    369                            SetBodyResponse result = new SetBodyResponse();
    370                            result.result = (SetBodyResult) decoder.getEnumParm(1, SetBodyResult.get(0), false);
    371                            result.chunks = decoder.getStringListParm(2, false);
    372                            result.charsUntilNextChunk = decoder.getUintParm(3, true);
    373                            return result;
    374                    } catch(IOException e) {
    375                            mSidRoot.sidOnFatalError(e);
    376                            return null
    377                    ;}
    378            }
    379            private final static byte[] getBodyChunks_req = {(byte) 90,(byte) 82,(byte) 12,(byte) 8};
    380            public class GetBodyChunksResponse {
    381                    public String[] textChunks;
    382                    public int charsUntilNextChunk;
    383            };
    384
    385            /** Retrieves string list of SMS text chunks in first argument, while the second argument contains the number of available characters until creation of the next chunk becomes necessary.  * @return GetBodyChunksResponse
    386             * <br> - textChunks List of text chunk strings
    387             * <br> - charsUntilNextChunk Number of available characters until creation of the next chunk becomes necessary.
    388             */
    389            public GetBodyChunksResponse getBodyChunks() {
    390                    try {
    391                            Decoding decoder = sidDoRequest(getBodyChunks_req)
    392                            .endRequest();
    393                            GetBodyChunksResponse result = new GetBodyChunksResponse();
    394                            result.textChunks = decoder.getStringListParm(1, false);
    395                            result.charsUntilNextChunk = decoder.getUintParm(2, true);
    396                            return result;
    397                    } catch(IOException e) {
    398                            mSidRoot.sidOnFatalError(e);
    399                            return null
    400                    ;}
    401            }
    402            /***
    403             * generic multiget of a list of Property
    404             * @param requested the list of requested properties of Sms
    405             * @return SidGetResponding
    406             */
    407            public SidGetResponding sidMultiGet(Property[] requested) {
    408                    return super.sidMultiGet(requested);
    409            }
    410            /***
    411             * generic multiget of list of Property for a list of Sms
    412             * @param requested the list of requested properties
    413             * @return SidGetResponding[] can be casted to (Sms[]) if all properties are cached
    414             */
    415            static public SidGetResponding[] sidMultiGet(Property[] requested, Sms[] objects) {
    416                    return SidObject.sidMultiGet(requested, objects);
    417            }
    418            public Type getType() {
    419                    synchronized(this) {
    420                            if ((mSidCached & 0x1) != 0)
    421                                    return mType;
    422                    }
    423                    return (Type) sidRequestEnumProperty(Property.P_TYPE);
    424            }
    425            public Status getStatus() {
    426                    synchronized(this) {
    427                            if ((mSidCached & 0x2) != 0)
    428                                    return mStatus;
    429                    }
    430                    return (Status) sidRequestEnumProperty(Property.P_STATUS);
    431            }
    432            /** Set asynchronously and meaningful only after invoking Conversation.PostSMS and detecting Sms.STATUS of SOME_TARGETS_FAILED or FAILED.  */
    433            public FailureReason getFailureReason() {
    434                    synchronized(this) {
    435                            if ((mSidCached & 0x4) != 0)
    436                                    return mFailureReason;
    437                    }
    438                    return (FailureReason) sidRequestEnumProperty(Property.P_FAILURE_REASON);
    439            }
    440            /** set to 1 when status goes to FAILED. use MarkSeen() to clear */
    441            public boolean getIsFailedUnseen() {
    442                    synchronized(this) {
    443                            if ((mSidCached & 0x8) != 0)
    444                                    return mIsFailedUnseen;
    445                    }
    446                    return sidRequestBoolProperty(Property.P_IS_FAILED_UNSEEN);
    447            }
    448            /** unix timestamp of message submission */
    449            public int getTimestamp() {
    450                    synchronized(this) {
    451                            if ((mSidCached & 0x10) != 0)
    452                                    return mTimestamp;
    453                    }
    454                    return sidRequestUintProperty(Property.P_TIMESTAMP);
    455            }
    456            /**
    457             * The total price of sending this SMS message (sum of the individual prices to send to each recipient). Defaults to -1 on instantiation and incremented by the price for each recipient once that recipient's status reflects TARGET_ACCEPTABLE. Use Sms.GetTargetPrice to retrieve individual target prices.
    458             *
    459             * A value of MAX_UINT indicates that SkypeKit is actively querying and/or updating the value. Note that P_PRICE is an integer value. Calculate the actual price (in units specified by P_PRICE_CURRENCY) using P_PRICE_PRECISION as:
    460             *
    461             * @code
    462             * actualPrice = price / 10^pricePrecision;
    463             * </CODE>
    464             */
    465            public int getPrice() {
    466                    synchronized(this) {
    467                            if ((mSidCached & 0x20) != 0)
    468                                    return mPrice;
    469                    }
    470                    return sidRequestUintProperty(Property.P_PRICE);
    471            }
    472            /** The decimal precision of the SMS price values, both individual and total. For example, a value of 2 indicates that you should divide the price (represented as an integer) by 100 (10^2) to obtain the actual price.  */
    473            public int getPricePrecision() {
    474                    synchronized(this) {
    475                            if ((mSidCached & 0x40) != 0)
    476                                    return mPricePrecision;
    477                    }
    478                    return sidRequestUintProperty(Property.P_PRICE_PRECISION);
    479            }
    480            /** should be same as account currency at the time of composing/sending */
    481            public String getPriceCurrency() {
    482                    synchronized(this) {
    483                            if ((mSidCached & 0x80) != 0)
    484                                    return mPriceCurrency;
    485                    }
    486                    return sidRequestStringProperty(Property.P_PRICE_CURRENCY);
    487            }
    488            /** number that should receive the replies */
    489            public String getReplyToNumber() {
    490                    synchronized(this) {
    491                            if ((mSidCached & 0x100) != 0)
    492                                    return mReplyToNumber;
    493                    }
    494                    return sidRequestStringProperty(Property.P_REPLY_TO_NUMBER);
    495            }
    496            /** space-separated normalised pstn numbers */
    497            public String getTargetNumbers() {
    498                    synchronized(this) {
    499                            if ((mSidCached & 0x200) != 0)
    500                                    return mTargetNumbers;
    501                    }
    502                    return sidRequestStringProperty(Property.P_TARGET_NUMBERS);
    503            }
    504            /** binary blob. track with OnPropertyChange(), access with GetTargetStatus(target) */
    505            public byte[] getTargetStatuses() {
    506                    synchronized(this) {
    507                            if ((mSidCached & 0x400) != 0)
    508                                    return mTargetStatuses;
    509                    }
    510                    return sidRequestBinaryProperty(Property.P_TARGET_STATUSES);
    511            }
    512            /** actual payload */
    513            public String getBody() {
    514                    synchronized(this) {
    515                            if ((mSidCached & 0x800) != 0)
    516                                    return mBody;
    517                    }
    518                    return sidRequestStringProperty(Property.P_BODY);
    519            }
    520            /** reference to Message */
    521            public Message getChatMsg() {
    522                    synchronized(this) {
    523                            if ((mSidCached & 0x1000) != 0)
    524                                    return mChatMsg;
    525                    }
    526                    return (Message) sidRequestObjectProperty(Property.P_CHAT_MSG);
    527            }
    528            public String sidGetStringProperty(final PropertyEnumConverting prop) {
    529                    switch(prop.getId()) {
    530                    case 194:
    531                            return mPriceCurrency;
    532                    case 199:
    533                            return mReplyToNumber;
    534                    case 195:
    535                            return mTargetNumbers;
    536                    case 197:
    537                            return mBody;
    538                    }
    539                    return "";
    540            }
    541            public SidObject sidGetObjectProperty(final PropertyEnumConverting prop) {
    542                    assert(prop.getId() == 840);
    543                    return mChatMsg;
    544            }
    545            public boolean sidGetBoolProperty(final PropertyEnumConverting prop) {
    546                    assert(prop.getId() == 48);
    547                    return mIsFailedUnseen;
    548            }
    549            public int sidGetIntProperty(final PropertyEnumConverting prop) {
    550                    switch(prop.getId()) {
    551                    case 198:
    552                            return mTimestamp;
    553                    case 193:
    554                            return mPrice;
    555                    case 49:
    556                            return mPricePrecision;
    557                    }
    558                    return 0;
    559            }
    560            public EnumConverting sidGetEnumProperty(final PropertyEnumConverting prop) {
    561                    switch(prop.getId()) {
    562                    case 190:
    563                            return mType;
    564                    case 191:
    565                            return mStatus;
    566                    case 192:
    567                            return mFailureReason;
    568                    }
    569                    return null;
    570            }
    571            public byte[] sidGetBinaryProperty(final PropertyEnumConverting prop) {
    572                    assert(prop.getId() == 196);
    573                    return mTargetStatuses;
    574            }
    575            public String getPropertyAsString(final int prop) {
    576                    switch (prop) {
    577                    case 190: return getType().toString();
    578                    case 191: return getStatus().toString();
    579                    case 192: return getFailureReason().toString();
    580                    case 48: return Boolean.toString(getIsFailedUnseen());
    581                    case 198: return Integer.toString(getTimestamp());
    582                    case 193: return Integer.toString(getPrice());
    583                    case 49: return Integer.toString(getPricePrecision());
    584                    case 194: return getPriceCurrency();
    585                    case 199: return getReplyToNumber();
    586                    case 195: return getTargetNumbers();
    587                    case 196: return "<binary>";
    588                    case 197: return getBody();
    589                    case 840: return Integer.toString(getChatMsg().getOid());
    590                    }
    591                    return "<unkown>";
    592            }
    593            public String getPropertyAsString(final Property prop) {
    594                    return getPropertyAsString(prop.getId());
    595            }
    596            protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue) {
    597                    final Property property = Property.get(propertyId);
    598                    if (property == Property.P_UNKNOWN) return;
    599                    final int idx = property.getIdx();
    600                    if (idx != 0) {
    601                            int bit  = 1<<((idx-1)%32);
    602                            synchronized (this) {
    603                                    mSidCached|=bit;                                switch (propertyId) {
    604                                    case 190: mType = Type.get(value); break;
    605                                    case 191: mStatus = Status.get(value); break;
    606                                    case 192: mFailureReason = FailureReason.get(value); break;
    607                                    case 48: mIsFailedUnseen = value != 0; break;
    608                                    case 198: mTimestamp = value; break;
    609                                    case 193: mPrice = value; break;
    610                                    case 49: mPricePrecision = value; break;
    611                                    case 194:
    612                                            if (svalue != null) mPriceCurrency = svalue;
    613                                            else mSidCached &=~bit;
    614                                            break;
    615                                    case 199:
    616                                            if (svalue != null) mReplyToNumber = svalue;
    617                                            else mSidCached &=~bit;
    618                                            break;
    619                                    case 195:
    620                                            if (svalue != null) mTargetNumbers = svalue;
    621                                            else mSidCached &=~bit;
    622                                            break;
    623                                    case 197:
    624                                            if (svalue != null) mBody = svalue;
    625                                            else mSidCached &=~bit;
    626                                            break;
    627                                    default: mSidCached|=bit; break;
    628                                    }
    629                            }
    630                    }
    631                    SmsListener listener = ((Skype) mSidRoot).getSmsListener();
    632                    if (listener != null)
    633                            listener.onPropertyChange(this, property, value, svalue);
    634            }
    635            public void sidSetProperty(final PropertyEnumConverting prop, final String newValue) {
    636                    final int propId = prop.getId();
    637                    switch(propId) {
    638                    case 194:
    639                            mSidCached |= 0x80;
    640                            mPriceCurrency=  newValue;
    641                            break;
    642                    case 199:
    643                            mSidCached |= 0x100;
    644                            mReplyToNumber=  newValue;
    645                            break;
    646                    case 195:
    647                            mSidCached |= 0x200;
    648                            mTargetNumbers=  newValue;
    649                            break;
    650                    case 197:
    651                            mSidCached |= 0x800;
    652                            mBody=  newValue;
    653                            break;
    654                    }
    655            }
    656            public void sidSetProperty(final PropertyEnumConverting prop, final SidObject newValue) {
    657                    final int propId = prop.getId();
    658                    assert(propId == 840);
    659                    mSidCached |= 0x1000;
    660                    mChatMsg= (Message) newValue;
    661            }
    662            public void sidSetProperty(final PropertyEnumConverting prop, final int newValue) {
    663                    final int propId = prop.getId();
    664                    switch(propId) {
    665                    case 190:
    666                            mSidCached |= 0x1;
    667                            mType= Type.get(newValue);
    668                            break;
    669                    case 191:
    670                            mSidCached |= 0x2;
    671                            mStatus= Status.get(newValue);
    672                            break;
    673                    case 192:
    674                            mSidCached |= 0x4;
    675                            mFailureReason= FailureReason.get(newValue);
    676                            break;
    677                    case 48:
    678                            mSidCached |= 0x8;
    679                            mIsFailedUnseen= newValue != 0;
    680                            break;
    681                    case 198:
    682                            mSidCached |= 0x10;
    683                            mTimestamp=  newValue;
    684                            break;
    685                    case 193:
    686                            mSidCached |= 0x20;
    687                            mPrice=  newValue;
    688                            break;
    689                    case 49:
    690                            mSidCached |= 0x40;
    691                            mPricePrecision=  newValue;
    692                            break;
    693                    }
    694            }
    695            public void sidSetProperty(final PropertyEnumConverting prop, final byte[] newValue) {
    696                    final int propId = prop.getId();
    697                    assert(propId == 196);
    698                    mSidCached |= 0x400;
    699                    mTargetStatuses=  newValue;
    700            }
    701            public Type          mType;
    702            public Status        mStatus;
    703            public FailureReason mFailureReason;
    704            public boolean       mIsFailedUnseen;
    705            public int           mTimestamp;
    706            public int           mPrice;
    707            public int           mPricePrecision;
    708            public String        mPriceCurrency;
    709            public String        mReplyToNumber;
    710            public String        mTargetNumbers;
    711            public byte[]        mTargetStatuses;
    712            public String        mBody;
    713            public Message       mChatMsg;
    714            public int moduleId() {
    715                    return 12;
    716            }
    717
    718            public Sms(final int oid, final SidRoot root) {
    719                    super(oid, root, 13);
    720            }
    721    }