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], 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 private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
099
100 static {
101 for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
102 lookup.put(s.getId(), s);
103 }
104
105 private final int id;
106
107 private PROPERTY(int value) {
108 this.id = value;
109 }
110
111 public int getId() { return id; }
112
113 public static PROPERTY get(int code) {
114 return lookup.get(code);
115 }
116
117 public static PROPERTY fromString(String s) {
118 for (PROPERTY p : lookup.values()) {
119 if (p.toString() == s) {
120 return p;
121 }
122 }
123 return null;
124 }
125 }
126
127 public Object GetPropertyAsEnum(int propid) {
128 return PROPERTY.get(propid);
129 }
130
131 public String GetStrProperty(PROPERTY prop) {
132 //check in propcache if so then return
133 if (mPropCache.containsKey(new Integer(prop.id))){
134 String value = (String)mPropCache.get(prop.id);
135 if (value != null && !(value.length() == 0) ){
136 return value;
137 }
138 }
139 //else get from skypekit...
140 GetPropertyRequest request = new GetPropertyRequest(19, mObjectId, prop.id);
141
142 String string = null;
143 GetPropertyResponse r = skype.GetProperty(request);
144 if (r != null){
145 string = r.GetAsString();
146 }
147
148 if (string != null)
149 {
150 mPropCache.put(new Integer(prop.id), string);
151 }
152 return string;
153 }
154
155 public int GetIntProperty(PROPERTY prop) {
156 //check in propcache if so then return
157 if (mPropCache.containsKey(new Integer(prop.id))){
158 int value = ((Integer)mPropCache.get(prop.id)).intValue();
159 if (value != 0){
160 return value;
161 }
162 }
163 //else get from skypekit...
164 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
165
166 Integer integer = null;
167 GetPropertyResponse r = skype.GetProperty(request);
168 if (r != null){
169 integer = r.GetAsInt();
170 }
171
172 if (integer != null)
173 {
174 mPropCache.put(new Integer(prop.id), integer);
175 return integer.intValue();
176 }
177 else
178 {
179 return 0;
180 }
181 }
182
183 public boolean GetBooleanProperty(PROPERTY prop) {
184 //check in propcache if so then return
185 if (mPropCache.containsKey(new Integer(prop.id))){
186 return ((Boolean)mPropCache.get(prop.id)).booleanValue();
187 }
188 //else get from skypekit...
189 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
190
191 Boolean boolResp = null;
192 GetPropertyResponse r = skype.GetProperty(request);
193 if (r != null){
194 boolResp = r.GetAsBoolean();
195 }
196
197 if (boolResp != null)
198 {
199 mPropCache.put(new Integer(prop.id), boolResp);
200 return boolResp.booleanValue();
201 }
202 else
203 {
204 return false;
205 }
206 }
207
208 public byte [] GetBinProperty(PROPERTY prop) {
209 //get from skypekit...
210 GetPropertyRequest request = new GetPropertyRequest(19, mObjectId, prop.id);
211
212 byte [] data = null;
213 GetPropertyResponse r = skype.GetProperty(request);
214 if (r != null) {
215 data = r.GetAsBinary();
216 }
217 return data;
218 }
219
220 /**default array of Participant Properties that get fetched & cached upon class construction*/
221 private static PROPERTY [] defaultProperties = { PROPERTY.convo_id, PROPERTY.rank, PROPERTY.identity, PROPERTY.adder};
222
223 private void GetDefaultProps() {
224 MultiGetPropertyRequest request = null;
225 ArrayList<Integer> proparray = null;
226 /**Add the single oid into array*/
227 ArrayList<Integer> oidarray=new ArrayList<Integer>();
228 oidarray.add(mObjectId);
229
230 /**Add all requested propids into array*/
231 proparray=new ArrayList<Integer>();
232 for (PROPERTY defaultProp : defaultProperties) {
233 proparray.add(defaultProp.getId());
234 }
235 /**Generate the request*/
236 request = new MultiGetPropertyRequest(moduleID(), oidarray,proparray);
237
238 /** Make Multi Get call*/
239 GetPropertyResponse r=skype.MultiGetProperty(request);
240 /**Verify that it is a proper multiresponse*/
241 if(!r.isMultiresponse())
242 {
243 return;
244 }
245 /**update property cache with results*/
246 mPropCache.putAll(r.GetAsMap(mObjectId, proparray));
247 }
248
249 /**
250 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> */
251 public enum RANK {
252
253 /** Creator of the chat. There can be only one participant with this type per conversation. Other participants cannot be promoted to Creator rank. <br>*/
254 CREATOR(1),
255
256 /** Participant who has administrator privileges <br>*/
257 ADMIN(2),
258
259 /** Participant who can speak and write <br>*/
260 SPEAKER(3),
261
262 /** Participant who can write but not speak <br>*/
263 WRITER(4),
264
265 /** Participant who can read but not write/speak <br>*/
266 SPECTATOR(5),
267
268 /** Participant who is applying to join the conversation. Member cannot be demoted to applicants once they have been accepted. <br>*/
269 APPLICANT(6),
270
271 /** Participant who has eft or has been kicked from the conversation <br>*/
272 RETIRED(7),
273
274 /** Participant who has been banned from the chat <br>*/
275 OUTLAW(8);
276
277 private static final Map<Integer,RANK> lookup = new HashMap<Integer,RANK>();
278
279 static {
280 for(RANK s : EnumSet.allOf(RANK.class))
281 lookup.put(s.getId(), s);
282 }
283
284 private final int id;
285
286 private RANK(int value) {
287 this.id = value;
288 }
289
290 public int getId() { return id; }
291
292 public static RANK get(int code) {
293 return lookup.get(code);
294 }
295
296 public static RANK fromString(String s) {
297 for (RANK p : lookup.values()) {
298 if (p.toString() == s) {
299 return p;
300 }
301 }
302 return null;
303 }
304 }
305
306 /**
307 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> */
308 public enum TEXT_STATUS {
309
310 /** Fallback state in case the text status is not (yet) deternmined. <br>*/
311 TEXT_UNKNOWN(0),
312
313 /** 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>*/
314 TEXT_NA(1),
315
316 /** Participant is currently not typing. <br>*/
317 READING(2),
318
319 /** Participant is currently typing. <br>*/
320 WRITING(3),
321
322 /** 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>*/
323 WRITING_AS_ANGRY(4),
324
325 /** 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>*/
326 WRITING_AS_CAT(5);
327
328 private static final Map<Integer,TEXT_STATUS> lookup = new HashMap<Integer,TEXT_STATUS>();
329
330 static {
331 for(TEXT_STATUS s : EnumSet.allOf(TEXT_STATUS.class))
332 lookup.put(s.getId(), s);
333 }
334
335 private final int id;
336
337 private TEXT_STATUS(int value) {
338 this.id = value;
339 }
340
341 public int getId() { return id; }
342
343 public static TEXT_STATUS get(int code) {
344 return lookup.get(code);
345 }
346
347 public static TEXT_STATUS fromString(String s) {
348 for (TEXT_STATUS p : lookup.values()) {
349 if (p.toString() == s) {
350 return p;
351 }
352 }
353 return null;
354 }
355 }
356
357 /**
358 Recognized values for the P_VOICE_STATUS property. <br> */
359 public enum VOICE_STATUS {
360
361 /** Participant voice status is not (yet) determined. <br>*/
362 VOICE_UNKNOWN(0),
363
364 /** Participant is using a Skype client with no audio capability. <br>*/
365 VOICE_NA(1),
366
367 /** Participant is using a Skype client that supports audio. <br>*/
368 VOICE_AVAILABLE(2),
369
370 /** Participant is in process of joining current live session. This is a transitional state. <br>*/
371 VOICE_CONNECTING(3),
372
373 /** Participant has been invited to join the live session but has not yet accepted. <br>*/
374 RINGING(4),
375
376 /** Participant is in process of joining current live session. This is another transitional state. <br>*/
377 EARLY_MEDIA(5),
378
379 /** Participant has joined the current live session but is currently not transmitting audio. <br>*/
380 LISTENING(6),
381
382 /** 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>*/
383 SPEAKING(7),
384
385 /** Participant has joined the current live session but the audio is currently on hold. <br>*/
386 VOICE_ON_HOLD(8),
387
388 /** Participant will be placed in this state for some seconds after live session has finished. This is another transitional state. <br>*/
389 VOICE_STOPPED(9);
390
391 private static final Map<Integer,VOICE_STATUS> lookup = new HashMap<Integer,VOICE_STATUS>();
392
393 static {
394 for(VOICE_STATUS s : EnumSet.allOf(VOICE_STATUS.class))
395 lookup.put(s.getId(), s);
396 }
397
398 private final int id;
399
400 private VOICE_STATUS(int value) {
401 this.id = value;
402 }
403
404 public int getId() { return id; }
405
406 public static VOICE_STATUS get(int code) {
407 return lookup.get(code);
408 }
409
410 public static VOICE_STATUS fromString(String s) {
411 for (VOICE_STATUS p : lookup.values()) {
412 if (p.toString() == s) {
413 return p;
414 }
415 }
416 return null;
417 }
418 }
419
420 /**
421 Recognized values for the P_VIDEO_STATUS property. This property applies to Participant's video send capability, not capability to receive video. <br> */
422 public enum VIDEO_STATUS {
423
424 /** Video status is not (yet) determined. <br>*/
425 VIDEO_UNKNOWN(0),
426
427 /** Indicates that this Participant does not have video available.. <br>*/
428 VIDEO_NA(1),
429
430 /** 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>*/
431 VIDEO_AVAILABLE(2),
432
433 /** Transitional state indicating that the Participant is attempting to initiate video send. <br>*/
434 VIDEO_CONNECTING(3),
435
436 /** Indicates that the participant is currently sending video. <br>*/
437 STREAMING(4),
438
439 /** Indicates that the participant video send is currently paused. <br>*/
440 VIDEO_ON_HOLD(5);
441
442 private static final Map<Integer,VIDEO_STATUS> lookup = new HashMap<Integer,VIDEO_STATUS>();
443
444 static {
445 for(VIDEO_STATUS s : EnumSet.allOf(VIDEO_STATUS.class))
446 lookup.put(s.getId(), s);
447 }
448
449 private final int id;
450
451 private VIDEO_STATUS(int value) {
452 this.id = value;
453 }
454
455 public int getId() { return id; }
456
457 public static VIDEO_STATUS get(int code) {
458 return lookup.get(code);
459 }
460
461 public static VIDEO_STATUS fromString(String s) {
462 for (VIDEO_STATUS p : lookup.values()) {
463 if (p.toString() == s) {
464 return p;
465 }
466 }
467 return null;
468 }
469 }
470
471 /**
472 */
473 public enum DTMF {
474
475 /** */
476 DTMF_0(0),
477
478 /** */
479 DTMF_1(1),
480
481 /** */
482 DTMF_2(2),
483
484 /** */
485 DTMF_3(3),
486
487 /** */
488 DTMF_4(4),
489
490 /** */
491 DTMF_5(5),
492
493 /** */
494 DTMF_6(6),
495
496 /** */
497 DTMF_7(7),
498
499 /** */
500 DTMF_8(8),
501
502 /** */
503 DTMF_9(9),
504
505 /** */
506 DTMF_STAR(10),
507
508 /** */
509 DTMF_POUND(11);
510
511 private static final Map<Integer,DTMF> lookup = new HashMap<Integer,DTMF>();
512
513 static {
514 for(DTMF s : EnumSet.allOf(DTMF.class))
515 lookup.put(s.getId(), s);
516 }
517
518 private final int id;
519
520 private DTMF(int value) {
521 this.id = value;
522 }
523
524 public int getId() { return id; }
525
526 public static DTMF get(int code) {
527 return lookup.get(code);
528 }
529
530 public static DTMF fromString(String s) {
531 for (DTMF p : lookup.values()) {
532 if (p.toString() == s) {
533 return p;
534 }
535 }
536 return null;
537 }
538 }
539
540 /**
541 *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>
542 * @param rank Participant.RANK value to check for. <br>
543 * @return result Returns true if local user can set participant's rank to the value given in rank argument. <br>
544 */
545 public boolean CanSetRankTo( RANK rank) {
546
547 Request request = null;
548 try {
549 request = new XCallRequest(19,1);
550 } catch (IOException e) {
551 e.printStackTrace();
552 if (skype.errorListener != null)
553 skype.errorListener.OnSkypeKitFatalError();
554 }
555 request.addParm('O',0,mObjectId);
556 request.addParm('e',1,rank.getId());
557
558 Response r = skype.XCall((XCallRequest)request);
559
560 if (r.isErrCall())
561 return false;
562
563 boolean result = false;
564 result = r.GetAsBoolean(1);
565 return result;
566 }
567
568 /**
569 *Sets Participant's conversation privileges to the given RANK <br>
570 * @param rank Target Participant.RANK value. <br>
571 */
572 public void SetRankTo( RANK rank) {
573
574 Request request = null;
575 try {
576 request = new XCallRequest(19,2);
577 } catch (IOException e) {
578 e.printStackTrace();
579 if (skype.errorListener != null)
580 skype.errorListener.OnSkypeKitFatalError();
581 }
582 request.addParm('O',0,mObjectId);
583 request.addParm('e',1,rank.getId());
584
585 skype.XCall((XCallRequest)request);
586 }
587
588 /**
589 *Initiates live conversation attempt with Participant. <br>
590 * @param identityToUse Ring an alternate identity, such as a PSTN number. <br>
591 * @param videoCall Enable video. <br>
592 * @param nrofRedials Unused. <br>
593 * @param redialPeriod Unused. <br>
594 * @param autoStartVM Unused. On dialog, if falling on VM, greeting and recording will be automatically started. <br>
595 * @param origin When call is initiated from web link, must contain the URI that was used
596 */
597 public void Ring( String identityToUse, boolean videoCall, int nrofRedials, int redialPeriod, boolean autoStartVM, String origin) {
598
599 Request request = null;
600 try {
601 request = new XCallRequest(19,3);
602 } catch (IOException e) {
603 e.printStackTrace();
604 if (skype.errorListener != null)
605 skype.errorListener.OnSkypeKitFatalError();
606 }
607 request.addParm('O',0,mObjectId);
608 request.addParm('S',1,identityToUse);
609 request.addParm('b',2,videoCall);
610 request.addParm('u',3,nrofRedials);
611 request.addParm('u',4,redialPeriod);
612 request.addParm('b',5,autoStartVM);
613 request.addParm('S',6,origin);
614
615 skype.XCall((XCallRequest)request);
616 }
617
618 /**
619 *Rings this participant, using P_LIVE_IDENTITY property if set. <br>
620 */
621 public void RingIt() {
622
623 Request request = null;
624 try {
625 request = new XCallRequest(19,8);
626 } catch (IOException e) {
627 e.printStackTrace();
628 if (skype.errorListener != null)
629 skype.errorListener.OnSkypeKitFatalError();
630 }
631 request.addParm('O',0,mObjectId);
632
633 skype.XCall((XCallRequest)request);
634 }
635
636 /**
637 *Sets LIVE_IDENTITY property, an alternate identity to use when ringing, such as a PSTN. <br>
638 * @param identityToUse Empty string will reset it to default, i.e IDENTITY property value <br>
639 */
640 public void SetLiveIdentityToUse( String identityToUse) {
641
642 Request request = null;
643 try {
644 request = new XCallRequest(19,7);
645 } catch (IOException e) {
646 e.printStackTrace();
647 if (skype.errorListener != null)
648 skype.errorListener.OnSkypeKitFatalError();
649 }
650 request.addParm('O',0,mObjectId);
651 request.addParm('S',1,identityToUse);
652
653 skype.XCall((XCallRequest)request);
654 }
655
656 /**
657 *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>
658 * @return video Returns reference to a constructed video object. <br>
659 */
660 public Video GetVideo() {
661
662 Request request = null;
663 try {
664 request = new XCallRequest(19,4);
665 } catch (IOException e) {
666 e.printStackTrace();
667 if (skype.errorListener != null)
668 skype.errorListener.OnSkypeKitFatalError();
669 }
670 request.addParm('O',0,mObjectId);
671
672 Response r = skype.XCall((XCallRequest)request);
673
674 if (r.isErrCall())
675 return null;
676
677 int oid = 0;
678 Video video = null;
679 oid = r.GetOid(1);
680 if (oid != AbstractDecoder.NULL_VALUE) {
681 video = (Video)skype.factory(Video.moduleID(), oid, skype);
682 }
683 return video;
684 }
685
686 /**
687 *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>
688 */
689 public void Hangup() {
690
691 Request request = null;
692 try {
693 request = new XCallRequest(19,5);
694 } catch (IOException e) {
695 e.printStackTrace();
696 if (skype.errorListener != null)
697 skype.errorListener.OnSkypeKitFatalError();
698 }
699 request.addParm('O',0,mObjectId);
700
701 skype.XCall((XCallRequest)request);
702 }
703
704 /**
705 *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>
706 */
707 public void Retire() {
708
709 Request request = null;
710 try {
711 request = new XCallRequest(19,6);
712 } catch (IOException e) {
713 e.printStackTrace();
714 if (skype.errorListener != null)
715 skype.errorListener.OnSkypeKitFatalError();
716 }
717 request.addParm('O',0,mObjectId);
718
719 skype.XCall((XCallRequest)request);
720 }
721
722
723 }