    001    package com.skype.api;
    002
    003    import java.io.IOException;
    004    import java.util.*;
    005    import com.skype.ipc.*;
    006    /**
    007     * 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.  <br><br>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: <br><br>@code <br>Conversation->Message->SMS  <br></CODE> <br><br>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. <br>
    008     */
    009
    010
    011    public class Sms extends SkypeObject {
    012
    013
    014            public interface SmsListener {
    015                    /** This event gets called when there are changes to Sms properties defined in Sms.PROPERTY  */
    016                    public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
    017
    018            }
    019
    020            public Sms(int oid, Skype skype) {
    021                    super(oid,skype);
    022            }
    023
    024            private static final int MODULE_ID = 12;
    025
    026            public static final int moduleID() {
    027                    return MODULE_ID;
    028            }
    029
    030            /** Properties of the Sms class */
    031            public enum PROPERTY {
    032
    033                    /** type: TYPE */
    034                    type(190),
    035
    036                    /** type: STATUS */
    037                    status(191),
    038
    039                    /** Set asynchronously and meaningful only after invoking Conversation.PostSMS and detecting Sms.STATUS of SOME_TARGETS_FAILED or FAILED. <br>, type: FAILUREREASON */
    040                    failurereason(192),
    041
    042                    /** set to 1 when status goes to FAILED. use MarkSeen() to clear, type: boolean */
    043                    is_failed_unseen(48),
    044
    045                    /** unix timestamp of message submission, type: int */
    046                    timestamp(198),
    047
    048                    /** 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. <br><br>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: <br><br>@code <br>actualPrice = price / 10^pricePrecision;  <br></CODE> <br>, type: int */
    049                    price(193),
    050
    051                    /** 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. <br>, type: int */
    052                    price_precision(49),
    053
    054                    /** should be same as account currency at the time of composing/sending, type: String */
    055                    price_currency(194),
    056
    057                    /** number that should receive the replies, type: String */
    058                    reply_to_number(199),
    059
    060                    /** space-separated normalised pstn numbers, type: String */
    061                    target_numbers(195),
    062
    063                    /** binary blob. track with OnPropertyChange(), access with GetTargetStatus(target), type: byte[] */
    064                    target_statuses(196),
    065
    066                    /** actual payload, type: String */
    067                    body(197),
    068
    069                    /** reference to Message, type: Message */
    070                    chatmsg_id(840);
    071
    072                    private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
    073
    074                    static {
    075                            for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
    076                                    lookup.put(s.getId(), s);
    077                    }
    078
    079                    private final int id;
    080
    081                    private PROPERTY(int value) {
    082                            this.id = value;
    083                    }
    084
    085                    public int getId() { return id; }
    086
    087                    public static PROPERTY get(int code) {
    088                            return lookup.get(code);
    089                    }
    090
    091                    public static PROPERTY fromString(String s) {
    092                            for (PROPERTY p : lookup.values()) {
    093                                    if (p.toString() == s) {
    094                                            return p;
    095                                    }
    096                            }
    097                            return null;
    098                    }
    099            }
    100
    101            public Object GetPropertyAsEnum(int propid) {
    102                    return PROPERTY.get(propid);
    103            }
    104
    105            public String GetStrProperty(PROPERTY prop) {
    106                    //check in propcache if so then return
    107                    if (mPropCache.containsKey(new Integer(prop.id))){
    108                            String value =  (String)mPropCache.get(prop.id);
    109                            if (value != null && !(value.length() == 0) ){
    110                                    return value;
    111                            }
    112                    }
    113                    //else get from skypekit...
    114                    GetPropertyRequest request = new GetPropertyRequest(12, mObjectId, prop.id);
    115
    116                    String string = null;
    117                    GetPropertyResponse r = skype.GetProperty(request);
    118                    if (r != null){
    119                            string = r.GetAsString();
    120                    }
    121
    122                    if (string != null)
    123                    {
    124                            mPropCache.put(new Integer(prop.id), string);
    125                    }
    126                    return string;
    127            }
    128
    129            public int GetIntProperty(PROPERTY prop) {
    130                    //check in propcache if so then return
    131                    if (mPropCache.containsKey(new Integer(prop.id))){
    132                            int value = ((Integer)mPropCache.get(prop.id)).intValue();
    133                            if (value != 0){
    134                                    return value;
    135                            }
    136                    }
    137                    //else get from skypekit...
    138                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
    139
    140                    Integer integer = null;
    141                    GetPropertyResponse r = skype.GetProperty(request);
    142                    if (r != null){
    143                            integer  = r.GetAsInt();
    144                    }
    145
    146                    if (integer != null)
    147                    {
    148                            mPropCache.put(new Integer(prop.id), integer);
    149                            return integer.intValue();
    150                    }
    151                    else
    152                    {
    153                            return 0;
    154                    }
    155            }
    156
    157            public boolean GetBooleanProperty(PROPERTY prop) {
    158                    //check in propcache if so then return
    159                    if (mPropCache.containsKey(new Integer(prop.id))){
    160                            return ((Boolean)mPropCache.get(prop.id)).booleanValue();
    161                    }
    162                    //else get from skypekit...
    163                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
    164
    165                    Boolean boolResp = null;
    166                    GetPropertyResponse r = skype.GetProperty(request);
    167                    if (r != null){
    168                            boolResp  = r.GetAsBoolean();
    169                    }
    170
    171                    if (boolResp != null)
    172                    {
    173                            mPropCache.put(new Integer(prop.id), boolResp);
    174                            return boolResp.booleanValue();
    175                    }
    176                    else
    177                    {
    178                            return false;
    179                    }
    180            }
    181
    182            public byte [] GetBinProperty(PROPERTY prop) {
    183                    //get from skypekit...
    184                    GetPropertyRequest request = new GetPropertyRequest(12, mObjectId, prop.id);
    185
    186                    byte [] data = null;
    187                    GetPropertyResponse r = skype.GetProperty(request);
    188                    if (r != null) {
    189                            data = r.GetAsBinary();
    190                    }
    191                    return data;
    192            }
    193
    194            /**
    195             */
    196            public enum TYPE {
    197
    198                    /** */
    199                    OUTGOING(2),
    200
    201                    /** */
    202                    CONFIRMATION_CODE_REQUEST(3),
    203
    204                    /** */
    205                    CONFIRMATION_CODE_SUBMIT(4);
    206
    207                    private static final Map<Integer,TYPE> lookup = new HashMap<Integer,TYPE>();
    208
    209                    static {
    210                            for(TYPE s : EnumSet.allOf(TYPE.class))
    211                                    lookup.put(s.getId(), s);
    212                    }
    213
    214                    private final int id;
    215
    216                    private TYPE(int value) {
    217                            this.id = value;
    218                    }
    219
    220                    public int getId() { return id; }
    221
    222                    public static TYPE get(int code) {
    223                            return lookup.get(code);
    224                    }
    225
    226                    public static TYPE fromString(String s) {
    227                            for (TYPE p : lookup.values()) {
    228                                    if (p.toString() == s) {
    229                                            return p;
    230                                    }
    231                            }
    232                            return null;
    233                    }
    234            }
    235
    236            /**
    237             */
    238            public enum STATUS {
    239
    240                    /** */
    241                    COMPOSING(3),
    242
    243                    /** */
    244                    SENDING_TO_SERVER(4),
    245
    246                    /** */
    247                    SENT_TO_SERVER(5),
    248
    249                    /** */
    250                    DELIVERED(6),
    251
    252                    /** */
    253                    SOME_TARGETS_FAILED(7),
    254
    255                    /** */
    256                    FAILED(8);
    257
    258                    private static final Map<Integer,STATUS> lookup = new HashMap<Integer,STATUS>();
    259
    260                    static {
    261                            for(STATUS s : EnumSet.allOf(STATUS.class))
    262                                    lookup.put(s.getId(), s);
    263                    }
    264
    265                    private final int id;
    266
    267                    private STATUS(int value) {
    268                            this.id = value;
    269                    }
    270
    271                    public int getId() { return id; }
    272
    273                    public static STATUS get(int code) {
    274                            return lookup.get(code);
    275                    }
    276
    277                    public static STATUS fromString(String s) {
    278                            for (STATUS p : lookup.values()) {
    279                                    if (p.toString() == s) {
    280                                            return p;
    281                                    }
    282                            }
    283                            return null;
    284                    }
    285            }
    286
    287            /**
    288             */
    289            public enum FAILUREREASON {
    290
    291                    /** */
    292                    MISC_ERROR(1),
    293
    294                    /** */
    295                    SERVER_CONNECT_FAILED(2),
    296
    297                    /** */
    298                    NO_SMS_CAPABILITY(3),
    299
    300                    /** */
    301                    INSUFFICIENT_FUNDS(4),
    302
    303                    /** */
    304                    INVALID_CONFIRMATION_CODE(5),
    305
    306                    /** */
    307                    USER_BLOCKED(6),
    308
    309                    /** */
    310                    IP_BLOCKED(7),
    311
    312                    /** */
    313                    NODE_BLOCKED(8),
    314
    315                    /** */
    316                    NO_SENDERID_CAPABILITY(9);
    317
    318                    private static final Map<Integer,FAILUREREASON> lookup = new HashMap<Integer,FAILUREREASON>();
    319
    320                    static {
    321                            for(FAILUREREASON s : EnumSet.allOf(FAILUREREASON.class))
    322                                    lookup.put(s.getId(), s);
    323                    }
    324
    325                    private final int id;
    326
    327                    private FAILUREREASON(int value) {
    328                            this.id = value;
    329                    }
    330
    331                    public int getId() { return id; }
    332
    333                    public static FAILUREREASON get(int code) {
    334                            return lookup.get(code);
    335                    }
    336
    337                    public static FAILUREREASON fromString(String s) {
    338                            for (FAILUREREASON p : lookup.values()) {
    339                                    if (p.toString() == s) {
    340                                            return p;
    341                                    }
    342                            }
    343                            return null;
    344                    }
    345            }
    346
    347            /**
    348             */
    349            public enum TARGETSTATUS {
    350
    351                    /** */
    352                    TARGET_ANALYZING(1),
    353
    354                    /** */
    355                    TARGET_UNDEFINED(2),
    356
    357                    /** */
    358                    TARGET_ACCEPTABLE(3),
    359
    360                    /** */
    361                    TARGET_NOT_ROUTABLE(4),
    362
    363                    /** */
    364                    TARGET_DELIVERY_PENDING(5),
    365
    366                    /** */
    367                    TARGET_DELIVERY_SUCCESSFUL(6),
    368
    369                    /** */
    370                    TARGET_DELIVERY_FAILED(7);
    371
    372                    private static final Map<Integer,TARGETSTATUS> lookup = new HashMap<Integer,TARGETSTATUS>();
    373
    374                    static {
    375                            for(TARGETSTATUS s : EnumSet.allOf(TARGETSTATUS.class))
    376                                    lookup.put(s.getId(), s);
    377                    }
    378
    379                    private final int id;
    380
    381                    private TARGETSTATUS(int value) {
    382                            this.id = value;
    383                    }
    384
    385                    public int getId() { return id; }
    386
    387                    public static TARGETSTATUS get(int code) {
    388                            return lookup.get(code);
    389                    }
    390
    391                    public static TARGETSTATUS fromString(String s) {
    392                            for (TARGETSTATUS p : lookup.values()) {
    393                                    if (p.toString() == s) {
    394                                            return p;
    395                                    }
    396                            }
    397                            return null;
    398                    }
    399            }
    400
    401            /**
    402             *Retrieves the send status of this SMS to a particular recipient (P_TARGET_STATUSES) either prior to or after invoking Conversation.PostSMS. <br>
    403             * @param target The normalized phone number of the target recipient. <br>
    404             * @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. <br>
    405             */
    406            public TARGETSTATUS GetTargetStatus( String target) {
    407
    408                    Request request = null;
    409                    try {
    410                            request = new XCallRequest(12,4);
    411                    } catch (IOException e) {
    412                            e.printStackTrace();
    413                            if (skype.errorListener != null)
    414                                    skype.errorListener.OnSkypeKitFatalError();
    415                    }
    416                    request.addParm('O',0,mObjectId);
    417                    request.addParm('S',1,target);
    418
    419                    Response r = skype.XCall((XCallRequest)request);
    420
    421                    if (r.isErrCall())
    422                            return null;
    423
    424                    TARGETSTATUS targetstatus = null;
    425                    targetstatus = TARGETSTATUS.get(r.GetAsInt(1));
    426                    return targetstatus;
    427            }
    428
    429            /**
    430             *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. <br><br>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: <br>@code <br>actualTargetPrice = targetPrice / 10^pricePrecision;  <br></CODE> <br>
    431             * @param target The normalized phone number of the target recipient. <br>
    432             * @return price The price of sending this SMS message to the target recipient. <br>
    433             */
    434            public int GetTargetPrice( String target) {
    435
    436                    Request request = null;
    437                    try {
    438                            request = new XCallRequest(12,13);
    439                    } catch (IOException e) {
    440                            e.printStackTrace();
    441                            if (skype.errorListener != null)
    442                                    skype.errorListener.OnSkypeKitFatalError();
    443                    }
    444                    request.addParm('O',0,mObjectId);
    445                    request.addParm('S',1,target);
    446
    447                    Response r = skype.XCall((XCallRequest)request);
    448
    449                    if (r.isErrCall())
    450                            return 0;
    451
    452                    int price = 0;
    453                    price = r.GetAsInt(1);
    454                    return price;
    455            }
    456
    457            /**
    458             */
    459            public enum SETBODYRESULT {
    460
    461                    /** body not set. message status wrong or invalid, or body not valid utf8 string*/
    462                    BODY_INVALID(0),
    463
    464                    /** body too long. set, but truncated. charsUntilNextChunk contains maxChunks value*/
    465                    BODY_TRUNCATED(1),
    466
    467                    /** body was set OK*/
    468                    BODY_OK(2),
    469
    470                    /** last unicode char was ignored, as some of the text would be deleted due to conversion*/
    471                    BODY_LASTCHAR_IGNORED(3);
    472
    473                    private static final Map<Integer,SETBODYRESULT> lookup = new HashMap<Integer,SETBODYRESULT>();
    474
    475                    static {
    476                            for(SETBODYRESULT s : EnumSet.allOf(SETBODYRESULT.class))
    477                                    lookup.put(s.getId(), s);
    478                    }
    479
    480                    private final int id;
    481
    482                    private SETBODYRESULT(int value) {
    483                            this.id = value;
    484                    }
    485
    486                    public int getId() { return id; }
    487
    488                    public static SETBODYRESULT get(int code) {
    489                            return lookup.get(code);
    490                    }
    491
    492                    public static SETBODYRESULT fromString(String s) {
    493                            for (SETBODYRESULT p : lookup.values()) {
    494                                    if (p.toString() == s) {
    495                                            return p;
    496                                    }
    497                            }
    498                            return null;
    499                    }
    500            }
    501
    502            /**
    503             *Sets the recipient(s) of this SMS. Note that each invocation replaces the target list and re-calculates all prices - they are not additive! <br>
    504             * @param numbers Normalized phone number(s) of the intended recipient(s). <br>
    505             * @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. <br>
    506             */
    507            public boolean SetTargets( String [] numbers) {
    508
    509                    Request request = null;
    510                    try {
    511                            request = new XCallRequest(12,6);
    512                    } catch (IOException e) {
    513                            e.printStackTrace();
    514                            if (skype.errorListener != null)
    515                                    skype.errorListener.OnSkypeKitFatalError();
    516                    }
    517                    request.addParm('O',0,mObjectId);
    518                    request.addListStart(1);
    519                    for (int i=0;i<numbers.length;i++) {
    520                            request.addParm('S',numbers[i]);
    521                    }
    522
    523                    Response r = skype.XCall((XCallRequest)request);
    524
    525                    if (r.isErrCall())
    526                            return false;
    527
    528                    boolean success = false;
    529                    success = r.GetAsBoolean(1);
    530                    return success;
    531            }
    532
    533            /**
    534             *-The- method for setting the body text of this SMS. While Conversation.PostSMS does have a body argument, that argument is currently unused. <br>
    535             * @param text Message body text. <br>
    536             * @return SetSMSBodyResult
    537             */
    538            public SetSMSBodyResult SetBody( String text) {
    539
    540                    Request request = null;
    541                    try {
    542                            request = new XCallRequest(12,7);
    543                    } catch (IOException e) {
    544                            e.printStackTrace();
    545                            if (skype.errorListener != null)
    546                                    skype.errorListener.OnSkypeKitFatalError();
    547                    }
    548                    request.addParm('O',0,mObjectId);
    549                    request.addParm('S',1,text);
    550
    551                    Response r = skype.XCall((XCallRequest)request);
    552
    553                    if (r.isErrCall())
    554                            return null;
    555
    556                    SetSMSBodyResult result = new SetSMSBodyResult();
    557
    558                    SETBODYRESULT setbodyresult = null;
    559                    setbodyresult = SETBODYRESULT.get(r.GetAsInt(1));
    560                    result.result = setbodyresult;
    561
    562                    Vector<String> chunks = new Vector<String>();
    563                    while (r.HasMore(2))
    564                    {
    565                            String string = null;
    566                            string  = r.GetAsString(2);
    567                            chunks.add(string);
    568                    }
    569                    result.chunks = chunks.toArray(new String[chunks.size()]);
    570
    571                    int charsUntilNextChunk = 0;
    572                    charsUntilNextChunk = r.GetAsInt(3);
    573                    result.charsUntilNextChunk = charsUntilNextChunk;
    574
    575                    return result;
    576            }
    577
    578            public class SetSMSBodyResult {
    579                    public SETBODYRESULT result; /** Whether the Message body was successfully set and if not, why not. <br> */
    580                    public String [] chunks; /** The Message body as a list of individual chunks. <br> */
    581                    public int charsUntilNextChunk; /** Number of available characters until creation of the next chunk becomes necessary. <br> */
    582            }
    583
    584            /**
    585             *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. <br>
    586             * @return GetBodyChunksResult
    587             */
    588            public GetBodyChunksResult GetBodyChunks() {
    589
    590                    Request request = null;
    591                    try {
    592                            request = new XCallRequest(12,8);
    593                    } catch (IOException e) {
    594                            e.printStackTrace();
    595                            if (skype.errorListener != null)
    596                                    skype.errorListener.OnSkypeKitFatalError();
    597                    }
    598                    request.addParm('O',0,mObjectId);
    599
    600                    Response r = skype.XCall((XCallRequest)request);
    601
    602                    if (r.isErrCall())
    603                            return null;
    604
    605                    GetBodyChunksResult result = new GetBodyChunksResult();
    606
    607                    Vector<String> textChunks = new Vector<String>();
    608                    while (r.HasMore(1))
    609                    {
    610                            String string = null;
    611                            string  = r.GetAsString(1);
    612                            textChunks.add(string);
    613                    }
    614                    result.textChunks = textChunks.toArray(new String[textChunks.size()]);
    615
    616                    int charsUntilNextChunk = 0;
    617                    charsUntilNextChunk = r.GetAsInt(2);
    618                    result.charsUntilNextChunk = charsUntilNextChunk;
    619
    620                    return result;
    621            }
    622
    623            public class GetBodyChunksResult {
    624                    public String [] textChunks; /** List of text chunk strings <br> */
    625                    public int charsUntilNextChunk; /** Number of available characters until creation of the next chunk becomes necessary. <br> */
    626            }
    627
    628            /**
    629             */
    630            public enum CONFIRM_TYPE {
    631
    632                    /** Confirm mobile number as SMS sender number*/
    633                    ID_SMS(1),
    634
    635                    /** Confirm mobile number as CLI for SkypeOut calls*/
    636                    ID_MOBILE(2),
    637
    638                    /** unused currently*/
    639                    ID_SKYPEIN(3);
    640
    641                    private static final Map<Integer,CONFIRM_TYPE> lookup = new HashMap<Integer,CONFIRM_TYPE>();
    642
    643                    static {
    644                            for(CONFIRM_TYPE s : EnumSet.allOf(CONFIRM_TYPE.class))
    645                                    lookup.put(s.getId(), s);
    646                    }
    647
    648                    private final int id;
    649
    650                    private CONFIRM_TYPE(int value) {
    651                            this.id = value;
    652                    }
    653
    654                    public int getId() { return id; }
    655
    656                    public static CONFIRM_TYPE get(int code) {
    657                            return lookup.get(code);
    658                    }
    659
    660                    public static CONFIRM_TYPE fromString(String s) {
    661                            for (CONFIRM_TYPE p : lookup.values()) {
    662                                    if (p.toString() == s) {
    663                                            return p;
    664                                    }
    665                            }
    666                            return null;
    667                    }
    668            }
    669
    670
    671    }