001 package com.skype.api;
002
003 import java.io.IOException;
004 import java.util.*;
005 import com.skype.ipc.*;
006 /**
007 * Conversation participant class. Instances of this class represent contacts when in the context of conversations. Amongst others, this class has a Ring method for requesting live status with the target contact. This class also holds typing indicator property and access rights for the contact in context of public conversations. <br>
008 */
009
010
011 public class Participant extends SkypeObject {
012
013
014 public interface ParticipantListener {
015 /** This event gets called when there are changes to Participant properties defined in Participant.PROPERTY */
016 public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
017
018 /**This event gets fired on receiving a DTMF signal sent by Participant. Note that this event will only fire if the Participant is also using a Skype client. Skype audio library does not monitor incoming voice streams for dial tones. DTMF events are propagated to remote participants via data channel. Incoming DTMF tones transmitted from, for example, mobile phones, will not cause this event to fire. In case of incoming DTMF signals from Skype clients, DTMF tones are also inserted into the audio stream. You don't have to inject those into local audio playback yourself. <br>*/
019 public void OnIncomingDTMF(SkypeObject obj, DTMF dtmf);
020
021 }
022
023 public Participant(int oid, Skype skype) {
024 super(oid,skype);
025 /**get default properties for this module **/
026 GetDefaultProps();
027 }
028
029 private static final int MODULE_ID = 19;
030
031 public static final int moduleID() {
032 return MODULE_ID;
033 }
034
035 /** Properties of the Participant class */
036 public enum PROPERTY {
037
038 /** [ALL] ID of corresponding conversation. Here and below, [ALL] tag indicates that the property has meaning and is set in context of all participants in the conversation. [OTHERS] tag has meaning only for participants who are not the local user. <br>, type: Conversation */
039 convo_id(930),
040
041 /** [ALL] skypename OR pstn_number OR namespace:identity, type: String */
042 identity(931),
043
044 /** [ALL] Participant.RANK, type: RANK */
045 rank(932),
046
047 /** Not set (should be: requested Participant.RANK, higher than the current one), type: RANK */
048 requested_rank(933),
049
050 /** [ALL] the typing indicator, type: TEXT_STATUS */
051 text_status(934),
052
053 /** [ALL] voice status, type: VOICE_STATUS */
054 voice_status(935),
055
056 /** [ALL] video status, type: VIDEO_STATUS */
057 video_status(936),
058
059 /** [ALL] identity that was used to establish current live session with that participant (can be different from participant identity), type: String */
060 live_identity(943),
061
062 /** [OTHERS] 'price_per_minute_float currency' - eg '0.01 EUR'. Note that this property obtains value only after the participant goes into live state. It cannot be used to display call rates before the call starts. <br>, type: String */
063 live_price_for_me(938),
064
065 /** [OTHERS] list of identities where the live session is being forwarded (if they are disclosed), space separated, type: String */
066 live_fwd_identities(948),
067
068 /** [ALL] time of joining the live session, type: int */
069 live_start_timestamp(939),
070
071 /** [ALL] current 'loudness' level when SPEAKING (0..10), type: int */
072 sound_level(941),
073
074 /** [OTHERS] call (audio and video) debug info, type: String */
075 debuginfo(942),
076
077 /** [OTHERS] DEPRECATED, use last_leavereason instead, type: String */
078 last_voice_error(947),
079
080 /** [ALL] space separated tokens values: CPU_INUSE CPU_SLOW CPU_HIGH HIGH_ECHO HIGH_NOISE MUTED_INPUT LOW_INPUT MUTED_INPUT_ACTIVITY FW_STRONG FW_BAD NOT_UDP CALL_BW_LOW RECORD_ERROR + values in video debug info, type: String */
081 quality_problems(949),
082
083 /** [ALL] participant type during livesession as specified in IDENTITYTYPE, type: contact.IDENTITYTYPE */
084 live_type(950),
085
086 /** [OTHERS] participant livesession country code - used for emergency calls only atm, type: String */
087 live_country(951),
088
089 /** [OTHERS] Transferor identity (transferee side) <br>, type: String */
090 transferred_by(952),
091
092 /** [OTHERS] Identity of recipient of transfer (transferor side, caller side) <br>, type: String */
093 transferred_to(953),
094
095 /** [ALL] Identity of the user who added this participant to the conversation, type: Sid.String <br>, type: String */
096 adder(954),
097
098 /** [OTHERS] last reason for leaving conversation or live session. provides an enum alternative to last_voice_error as well as the reason this participant RETIRED, if so, type: LEAVE_REASON */
099 last_leavereason(955);
100
101 private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
102
103 static {
104 for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
105 lookup.put(s.getId(), s);
106 }
107
108 private final int id;
109
110 private PROPERTY(int value) {
111 this.id = value;
112 }
113
114 public int getId() { return id; }
115
116 public static PROPERTY get(int code) {
117 return lookup.get(code);
118 }
119
120 public static PROPERTY fromString(String s) {
121 for (PROPERTY p : lookup.values()) {
122 if (p.toString() == s) {
123 return p;
124 }
125 }
126 return null;
127 }
128 }
129
130 public Object GetPropertyAsEnum(int propid) {
131 return PROPERTY.get(propid);
132 }
133
134 public String GetStrProperty(PROPERTY prop) {
135 //check in propcache if so then return
136 if (mPropCache.containsKey(new Integer(prop.id))){
137 String value = (String)mPropCache.get(prop.id);
138 if (value != null && !(value.length() == 0) ){
139 return value;
140 }
141 }
142 //else get from skypekit...
143 GetPropertyRequest request = new GetPropertyRequest(19, mObjectId, prop.id);
144
145 String string = null;
146 GetPropertyResponse r = skype.GetProperty(request);
147 if (r != null){
148 string = r.GetAsString();
149 }
150
151 if (string != null)
152 {
153 mPropCache.put(new Integer(prop.id), string);
154 }
155 return string;
156 }
157
158 public int GetIntProperty(PROPERTY prop) {
159 //check in propcache if so then return
160 if (mPropCache.containsKey(new Integer(prop.id))){
161 int value = ((Integer)mPropCache.get(prop.id)).intValue();
162 if (value != 0){
163 return value;
164 }
165 }
166 //else get from skypekit...
167 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
168
169 Integer integer = null;
170 GetPropertyResponse r = skype.GetProperty(request);
171 if (r != null){
172 integer = r.GetAsInt();
173 }
174
175 if (integer != null)
176 {
177 mPropCache.put(new Integer(prop.id), integer);
178 return integer.intValue();
179 }
180 else
181 {
182 return 0;
183 }
184 }
185
186 public boolean GetBooleanProperty(PROPERTY prop) {
187 //check in propcache if so then return
188 if (mPropCache.containsKey(new Integer(prop.id))){
189 return ((Boolean)mPropCache.get(prop.id)).booleanValue();
190 }
191 //else get from skypekit...
192 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
193
194 Boolean boolResp = null;
195 GetPropertyResponse r = skype.GetProperty(request);
196 if (r != null){
197 boolResp = r.GetAsBoolean();
198 }
199
200 if (boolResp != null)
201 {
202 mPropCache.put(new Integer(prop.id), boolResp);
203 return boolResp.booleanValue();
204 }
205 else
206 {
207 return false;
208 }
209 }
210
211 public byte [] GetBinProperty(PROPERTY prop) {
212 //get from skypekit...
213 GetPropertyRequest request = new GetPropertyRequest(19, mObjectId, prop.id);
214
215 byte [] data = null;
216 GetPropertyResponse r = skype.GetProperty(request);
217 if (r != null) {
218 data = r.GetAsBinary();
219 }
220 return data;
221 }
222
223 /**default array of Participant Properties that get fetched & cached upon class construction*/
224 private static PROPERTY [] defaultProperties = { PROPERTY.convo_id, PROPERTY.rank, PROPERTY.identity, PROPERTY.adder};
225
226 private void GetDefaultProps() {
227 MultiGetPropertyRequest request = null;
228 ArrayList<Integer> proparray = null;
229 /**Add the single oid into array*/
230 ArrayList<Integer> oidarray=new ArrayList<Integer>();
231 oidarray.add(mObjectId);
232
233 /**Add all requested propids into array*/
234 proparray=new ArrayList<Integer>();
235 for (PROPERTY defaultProp : defaultProperties) {
236 proparray.add(defaultProp.getId());
237 }
238 /**Generate the request*/
239 request = new MultiGetPropertyRequest(moduleID(), oidarray,proparray);
240
241 /** Make Multi Get call*/
242 GetPropertyResponse r=skype.MultiGetProperty(request);
243 /**Verify that it is a proper multiresponse*/
244 if(!r.isMultiresponse())
245 {
246 return;
247 }
248 /**update property cache with results*/
249 mPropCache.putAll(r.GetAsMap(mObjectId, proparray));
250 }
251
252 /**
253 Recognized values for the P_RANK property. The P_RANK controls participant's privileges in the conversation. See Participant.CanSetRankTo and Participant.SetRankTo methods. <br> */
254 public enum RANK {
255
256 /** Creator of the chat. There can be only one participant with this type per conversation. Other participants cannot be promoted to Creator rank. <br>*/
257 CREATOR(1),
258
259 /** Participant who has administrator privileges <br>*/
260 ADMIN(2),
261
262 /** Participant who can speak and write <br>*/
263 SPEAKER(3),
264
265 /** Participant who can write but not speak <br>*/
266 WRITER(4),
267
268 /** Participant who can read but not write/speak <br>*/
269 SPECTATOR(5),
270
271 /** Participant who is applying to join the conversation. Member cannot be demoted to applicants once they have been accepted. <br>*/
272 APPLICANT(6),
273
274 /** Participant who has eft or has been kicked from the conversation <br>*/
275 RETIRED(7),
276
277 /** Participant who has been banned from the chat <br>*/
278 OUTLAW(8);
279
280 private static final Map<Integer,RANK> lookup = new HashMap<Integer,RANK>();
281
282 static {
283 for(RANK s : EnumSet.allOf(RANK.class))
284 lookup.put(s.getId(), s);
285 }
286
287 private final int id;
288
289 private RANK(int value) {
290 this.id = value;
291 }
292
293 public int getId() { return id; }
294
295 public static RANK get(int code) {
296 return lookup.get(code);
297 }
298
299 public static RANK fromString(String s) {
300 for (RANK p : lookup.values()) {
301 if (p.toString() == s) {
302 return p;
303 }
304 }
305 return null;
306 }
307 }
308
309 /**
310 Recognized values for the P_TEXT_STATUS property. The P_TEXT_STATUS property has two uses. Firstly, you can use it to implement typing indicators in your UI, to notify the local user that an incoming chat message from this Participant is imminent. <br><br>To set the P_TEXT_STATUS value, so that remote client UIs can display the local user's typing indicator in their UI, use Conversation.SetMyTextStatusTo method. <br><br>Transmission of P_TEXT_STATUS updates to remote participants of conversations is controlled via SETUPKEY_DISABLE_CHAT_ACTIVITY_INDICATION setup key. <br><br>Secondly, the TEXT_NA value enables you to detect participants who are running clients with no chat capability. <br> */
311 public enum TEXT_STATUS {
312
313 /** Fallback state in case the text status is not (yet) deternmined. <br>*/
314 TEXT_UNKNOWN(0),
315
316 /** Text status is not applicable as the participant is using a Skype client that does not support chat (for example: voice-only USB phones). <br>*/
317 TEXT_NA(1),
318
319 /** Participant is currently not typing. <br>*/
320 READING(2),
321
322 /** Participant is currently typing. <br>*/
323 WRITING(3),
324
325 /** This state should be set when following two conditions are true: <br> - interval between keypresses are less than 20 ms; <br> - at least one of the keys adjacent to current key are pressed down. <br>*/
326 WRITING_AS_ANGRY(4),
327
328 /** The "Cat on keyboard detection" algorthm in Skype is implemented in the UI level, and as such is not present in the SkypeKit API. Should you wish to implement similar algorthm in your own UI, you can get the basic logic from the PawSense FAQ - http://www.bitboost.com/pawsense/pawsense-faq.html <br>*/
329 WRITING_AS_CAT(5);
330
331 private static final Map<Integer,TEXT_STATUS> lookup = new HashMap<Integer,TEXT_STATUS>();
332
333 static {
334 for(TEXT_STATUS s : EnumSet.allOf(TEXT_STATUS.class))
335 lookup.put(s.getId(), s);
336 }
337
338 private final int id;
339
340 private TEXT_STATUS(int value) {
341 this.id = value;
342 }
343
344 public int getId() { return id; }
345
346 public static TEXT_STATUS get(int code) {
347 return lookup.get(code);
348 }
349
350 public static TEXT_STATUS fromString(String s) {
351 for (TEXT_STATUS p : lookup.values()) {
352 if (p.toString() == s) {
353 return p;
354 }
355 }
356 return null;
357 }
358 }
359
360 /**
361 Recognized values for the P_VOICE_STATUS property. <br> */
362 public enum VOICE_STATUS {
363
364 /** Participant voice status is not (yet) determined. <br>*/
365 VOICE_UNKNOWN(0),
366
367 /** Participant is using a Skype client with no audio capability. <br>*/
368 VOICE_NA(1),
369
370 /** Participant is using a Skype client that supports audio. <br>*/
371 VOICE_AVAILABLE(2),
372
373 /** Participant is in process of joining current live session. This is a transitional state. <br>*/
374 VOICE_CONNECTING(3),
375
376 /** Participant has been invited to join the live session but has not yet accepted. <br>*/
377 RINGING(4),
378
379 /** Participant is in process of joining current live session. This is another transitional state. <br>*/
380 EARLY_MEDIA(5),
381
382 /** Participant has joined the current live session but is currently not transmitting audio. <br>*/
383 LISTENING(6),
384
385 /** Participant has joined the current live session and is transmitting audio. The UI can either use this state to display appropriate "speaking" notification, or alternatively use Participant.P_SOUND_LEVEL if you want your speaking indicator to also reflect audio volume coming from the Participant. <br>*/
386 SPEAKING(7),
387
388 /** Participant has joined the current live session but the audio is currently on hold. <br>*/
389 VOICE_ON_HOLD(8),
390
391 /** Participant will be placed in this state for some seconds after live session has finished. This is another transitional state. <br>*/
392 VOICE_STOPPED(9);
393
394 private static final Map<Integer,VOICE_STATUS> lookup = new HashMap<Integer,VOICE_STATUS>();
395
396 static {
397 for(VOICE_STATUS s : EnumSet.allOf(VOICE_STATUS.class))
398 lookup.put(s.getId(), s);
399 }
400
401 private final int id;
402
403 private VOICE_STATUS(int value) {
404 this.id = value;
405 }
406
407 public int getId() { return id; }
408
409 public static VOICE_STATUS get(int code) {
410 return lookup.get(code);
411 }
412
413 public static VOICE_STATUS fromString(String s) {
414 for (VOICE_STATUS p : lookup.values()) {
415 if (p.toString() == s) {
416 return p;
417 }
418 }
419 return null;
420 }
421 }
422
423 /**
424 Recognized values for the P_VIDEO_STATUS property. This property applies to Participant's video send capability, not capability to receive video. <br> */
425 public enum VIDEO_STATUS {
426
427 /** Video status is not (yet) determined. <br>*/
428 VIDEO_UNKNOWN(0),
429
430 /** Indicates that this Participant does not have video available.. <br>*/
431 VIDEO_NA(1),
432
433 /** Indicates that video is available for this participant. When the Participant.P_VIDEO_STATUS obtains this state, it is possible to retrieve the Video object, using Participant.GetVideo method. Further operations, such as starting or stopping the video send/receive will then go through the Video object. <br>*/
434 VIDEO_AVAILABLE(2),
435
436 /** Transitional state indicating that the Participant is attempting to initiate video send. <br>*/
437 VIDEO_CONNECTING(3),
438
439 /** Indicates that the participant is currently sending video. <br>*/
440 STREAMING(4),
441
442 /** Indicates that the participant video send is currently paused. <br>*/
443 VIDEO_ON_HOLD(5);
444
445 private static final Map<Integer,VIDEO_STATUS> lookup = new HashMap<Integer,VIDEO_STATUS>();
446
447 static {
448 for(VIDEO_STATUS s : EnumSet.allOf(VIDEO_STATUS.class))
449 lookup.put(s.getId(), s);
450 }
451
452 private final int id;
453
454 private VIDEO_STATUS(int value) {
455 this.id = value;
456 }
457
458 public int getId() { return id; }
459
460 public static VIDEO_STATUS get(int code) {
461 return lookup.get(code);
462 }
463
464 public static VIDEO_STATUS fromString(String s) {
465 for (VIDEO_STATUS p : lookup.values()) {
466 if (p.toString() == s) {
467 return p;
468 }
469 }
470 return null;
471 }
472 }
473
474 /**
475 */
476 public enum DTMF {
477
478 /** */
479 DTMF_0(0),
480
481 /** */
482 DTMF_1(1),
483
484 /** */
485 DTMF_2(2),
486
487 /** */
488 DTMF_3(3),
489
490 /** */
491 DTMF_4(4),
492
493 /** */
494 DTMF_5(5),
495
496 /** */
497 DTMF_6(6),
498
499 /** */
500 DTMF_7(7),
501
502 /** */
503 DTMF_8(8),
504
505 /** */
506 DTMF_9(9),
507
508 /** */
509 DTMF_STAR(10),
510
511 /** */
512 DTMF_POUND(11);
513
514 private static final Map<Integer,DTMF> lookup = new HashMap<Integer,DTMF>();
515
516 static {
517 for(DTMF s : EnumSet.allOf(DTMF.class))
518 lookup.put(s.getId(), s);
519 }
520
521 private final int id;
522
523 private DTMF(int value) {
524 this.id = value;
525 }
526
527 public int getId() { return id; }
528
529 public static DTMF get(int code) {
530 return lookup.get(code);
531 }
532
533 public static DTMF fromString(String s) {
534 for (DTMF p : lookup.values()) {
535 if (p.toString() == s) {
536 return p;
537 }
538 }
539 return null;
540 }
541 }
542
543 /**
544 *Checks whether the current user can set this Participant's conversation privileges to the specified RANK. This enables you to gray out or disable in your UI all the unavailable options for Participant.SetRankTo method. <br>
545 * @param rank Participant.RANK value to check for. <br>
546 * @return result Returns true if local user can set participant's rank to the value given in rank argument. <br>
547 */
548 public boolean CanSetRankTo( RANK rank) {
549
550 Request request = null;
551 try {
552 request = new XCallRequest(19,1);
553 } catch (IOException e) {
554 e.printStackTrace();
555 if (skype.errorListener != null)
556 skype.errorListener.OnSkypeKitFatalError();
557 }
558 request.addParm('O',0,mObjectId);
559 request.addParm('e',1,rank.getId());
560
561 Response r = skype.XCall((XCallRequest)request);
562
563 if (r.isErrCall())
564 return false;
565
566 boolean result = false;
567 result = r.GetAsBoolean(1);
568 return result;
569 }
570
571 /**
572 *Sets Participant's conversation privileges to the given RANK <br>
573 * @param rank Target Participant.RANK value. <br>
574 */
575 public void SetRankTo( RANK rank) {
576
577 Request request = null;
578 try {
579 request = new XCallRequest(19,2);
580 } catch (IOException e) {
581 e.printStackTrace();
582 if (skype.errorListener != null)
583 skype.errorListener.OnSkypeKitFatalError();
584 }
585 request.addParm('O',0,mObjectId);
586 request.addParm('e',1,rank.getId());
587
588 skype.XCall((XCallRequest)request);
589 }
590
591 /**
592 *Initiates live conversation attempt with Participant. <br>
593 * @param identityToUse Ring an alternate identity, such as a PSTN number. <br>
594 * @param videoCall Enable video. <br>
595 * @param nrofRedials Unused. <br>
596 * @param redialPeriod Unused. <br>
597 * @param autoStartVM Unused. On dialog, if falling on VM, greeting and recording will be automatically started. <br>
598 * @param origin When call is initiated from web link, must contain the URI that was used
599 */
600 public void Ring( String identityToUse, boolean videoCall, int nrofRedials, int redialPeriod, boolean autoStartVM, String origin) {
601
602 Request request = null;
603 try {
604 request = new XCallRequest(19,3);
605 } catch (IOException e) {
606 e.printStackTrace();
607 if (skype.errorListener != null)
608 skype.errorListener.OnSkypeKitFatalError();
609 }
610 request.addParm('O',0,mObjectId);
611 request.addParm('S',1,identityToUse);
612 request.addParm('b',2,videoCall);
613 request.addParm('u',3,nrofRedials);
614 request.addParm('u',4,redialPeriod);
615 request.addParm('b',5,autoStartVM);
616 request.addParm('S',6,origin);
617
618 skype.XCall((XCallRequest)request);
619 }
620
621 /**
622 *Rings this participant, using P_LIVE_IDENTITY property if set. <br>
623 */
624 public void RingIt() {
625
626 Request request = null;
627 try {
628 request = new XCallRequest(19,8);
629 } catch (IOException e) {
630 e.printStackTrace();
631 if (skype.errorListener != null)
632 skype.errorListener.OnSkypeKitFatalError();
633 }
634 request.addParm('O',0,mObjectId);
635
636 skype.XCall((XCallRequest)request);
637 }
638
639 /**
640 *Sets LIVE_IDENTITY property, an alternate identity to use when ringing, such as a PSTN. <br>
641 * @param identityToUse Empty string will reset it to default, i.e IDENTITY property value <br>
642 */
643 public void SetLiveIdentityToUse( String identityToUse) {
644
645 Request request = null;
646 try {
647 request = new XCallRequest(19,7);
648 } catch (IOException e) {
649 e.printStackTrace();
650 if (skype.errorListener != null)
651 skype.errorListener.OnSkypeKitFatalError();
652 }
653 request.addParm('O',0,mObjectId);
654 request.addParm('S',1,identityToUse);
655
656 skype.XCall((XCallRequest)request);
657 }
658
659 /**
660 *Retrieves a reference to the Video object that corresponds to the Participant. It can be either local video - you can check if this participant's name (P_IDENTITY property) matches the name of the currently logged in account (P_SKYPENAME property) or incoming video from a remote participant. <br><br>Note that for GetVideo to be successful, the video has to be available for that participant. This can be checked for by examining Participant VIDEO_STATUS property - once it becomes VIDEO_AVAILABLE - you can use GetVideo to obtain the Video object. <br>
661 * @return video Returns reference to a constructed video object. <br>
662 */
663 public Video GetVideo() {
664
665 Request request = null;
666 try {
667 request = new XCallRequest(19,4);
668 } catch (IOException e) {
669 e.printStackTrace();
670 if (skype.errorListener != null)
671 skype.errorListener.OnSkypeKitFatalError();
672 }
673 request.addParm('O',0,mObjectId);
674
675 Response r = skype.XCall((XCallRequest)request);
676
677 if (r.isErrCall())
678 return null;
679
680 int oid = 0;
681 Video video = null;
682 oid = r.GetOid(1);
683 if (oid != AbstractDecoder.NULL_VALUE) {
684 video = (Video)skype.factory(Video.moduleID(), oid, skype);
685 }
686 return video;
687 }
688
689 /**
690 *Removes this participant from the current live session. Note that this does not remove the participant from conversation (for this, use Participant.Retire). It only removes participant from live state. <br>
691 */
692 public void Hangup() {
693
694 Request request = null;
695 try {
696 request = new XCallRequest(19,5);
697 } catch (IOException e) {
698 e.printStackTrace();
699 if (skype.errorListener != null)
700 skype.errorListener.OnSkypeKitFatalError();
701 }
702 request.addParm('O',0,mObjectId);
703
704 skype.XCall((XCallRequest)request);
705 }
706
707 /**
708 *Forcibly removes this participant from conversation. This method is for removing other people from conversations (for example: as administrative punishment for flooding conversation with spam messages). For local user to leave a conversation, use Conversation.RetireFrom instead. <br>
709 */
710 public void Retire() {
711
712 Request request = null;
713 try {
714 request = new XCallRequest(19,6);
715 } catch (IOException e) {
716 e.printStackTrace();
717 if (skype.errorListener != null)
718 skype.errorListener.OnSkypeKitFatalError();
719 }
720 request.addParm('O',0,mObjectId);
721
722 skype.XCall((XCallRequest)request);
723 }
724
725
726 }