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    }