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 /** a normal outgoing SMS message*/
199 OUTGOING(2),
200
201 /** a message requesting a SMS confirmation code be sent to the number provided*/
202 CONFIRMATION_CODE_REQUEST(3),
203
204 /** a message returning the SMS confirmation code received as a result of a CONFIRMATION_CODE_REQUEST to authorize it*/
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 }