001 package com.skype.api;
002
003 import java.io.IOException;
004 import java.util.*;
005 import com.skype.ipc.*;
006 /**
007 * Transfer in this context refers to transferring (sending/receiving) files among Skype Contacts, not transferring a call to another Skype or PSTN Contact. This class includes file transfer-specific properties and methods, such as FILESIZE, BYTESPERSECOND, Pause and Resume. Recipients of these file transfers must explicitly accept (or decline) any incoming transfer. Transfer instances represent files being sent and received within a Conversation context. Each Transfer instance represents a single file transfer - if a conversation has multiple remote participants, a separate Transfer instance must be instantiated for each remote participant (a Transfer instance is not instantiated for the sender). <br><br>Transfer instances cannot be instantiated directly. Instead, you initiate a file transfer by invoking Conversation.PostFiles. This instantiates a Message instance of type POSTED_FILES, which is added to the Conversation for all the participants (including the sender). The Transfer instance is associated with <br>this Message instance, and the Message.P_BODY_XML looks like this: <br><br>@code <br>Some text<files alt=""><file size="2336020" index="0">test.zip</file></files> <br></CODE> <br><br>To put it another way, the object chain goes like this: <br>@code <br>Conversation->Message->Transfer <br></CODE> <br><br>The first part of the message (before the files section) comes from the Conversation.PostFiles body argument. For each file in the message, a file section is provided with three fields: <br> - file size in bytes <br> - index - more or less arbitrary order in which the transfers should be displayed in the UI <br> - file name. <br><br>For practical purposes, the Message.P_BODY_XML property is not all that useful in this context. The Transfer instances, however, contain the state and progress feedback for your UI. You retrieve these Transfer instances using Message.GetTransfers method. Since the sender can post multiple files to <br>a Conversation using the same Message, Message:GetTransfers retrieves a list of Transfer instances - one per file per recipient. <br><br>You can determine the direction of particular Transfer instance by querying Transfer.P_TYPE (INCOMING/OUTGOING). <br><br>You can implement a progress indicator by monitoring Transfer.P_BYTESTRANSFERRED. Note that when testing this on your local network, you will most likely catch these property change events at the beginning and the end of the transfer only - which does not look like too useful. However, for non-local network transfers where the transfer speeds are in the neighborhood of 200-300 KB per second, you should consider implementing progress feedback as being mandatory and expect to catch multiple property change events for all but the smallest files. <br><br>Another property of interest is Transfer.P_STATUS. The normal transfer status sequence during successful outgoing transfer is this: <br> - TRANSFER STATUS -> NEW <br> - TRANSFER STATUS -> WAITING_FOR_ACCEPT <br> - TRANSFER STATUS -> CONNECTING <br> - TRANSFER STATUS -> TRANSFERRING <br> - TRANSFER STATUS -> CONNECTING <br> - TRANSFER STATUS -> COMPLETED <br><br>The list of all terminal Transfer statuses is: <br> - COMPLETED <br> - FAILED <br> - CANCELLED <br> - CANCELLED_BY_REMOTE <br><br>In-progress transfers can be canceled with Transfer.Cancel and paused/resumed with Transfer.Pause and Transfer.Resume. For transfers that complete with a status of FAILED, your UI should provide feedback based on the value of Transfer.P_FAILUREREASON. <br><br>Incoming transfers, once accepted, overwrite existing files with the same name. Before accepting an incoming file transfer, <br>your UI should prompt the recipient to: <br> - accept or decline the file <br> - if accepted, specify the directory of here to save the file (with a pre-filled default) <br> - if accepted and a file having the same name already exists at the specified destination, your UI should prompt for confirmation to overwrite and provide a way to alter the file name before accepting it <br><br>Similarly, your UI should verify the existence of outgoing files prior to invoking Conversation.PostFiles. <br><br>Note that you should provide both Conversation.PostFiles and Transfer.Accept methods fully-qualified paths. Otherwise, the paths will be assumed to be relative to the path of SkypeKit runtime, since the methods are actually executed in the runtime context. <br>
008 */
009
010
011 public class Transfer extends SkypeObject {
012
013
014 public interface TransferListener {
015 /** This event gets called when there are changes to Transfer properties defined in Transfer.PROPERTY */
016 public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
017
018 }
019
020 public Transfer(int oid, Skype skype) {
021 super(oid,skype);
022 }
023
024 private static final int MODULE_ID = 6;
025
026 public static final int moduleID() {
027 return MODULE_ID;
028 }
029
030 /** Properties of the Transfer class */
031 public enum PROPERTY {
032
033 /** INCOMING / OUTGOING <br>, type: TYPE */
034 type(80),
035
036 /** Skype Name of the remote party of the file transfer. If a file is posted in a conversation with more than one participant, Transfer objects are created for each of them - so a transfer is always to one single remote target. <br>, type: String */
037 partner_handle(81),
038
039 /** Display name of the remote participant. <br>, type: String */
040 partner_dispname(82),
041
042 /** Current state of the transfer <br>, type: STATUS */
043 status(83),
044
045 /** Set whenever P_STATUS transitions to FAILED. <br>, type: FAILUREREASON */
046 failurereason(84),
047
048 /** UNIX timestamp of when this Transfer instance was instantiated, not when the transfer process actually started (was accepted from receiver side). Do not use this property when calculate the data transfer speed! Instead, monitor changes to P_BYTESPERSECOND. <br>, type: int */
049 starttime(85),
050
051 /** UNIX timestamp of when this Transfer COMPLETED or FAILED. This property is never set if the receiving side (local or remote) canceled the transfer. <br>, type: int */
052 finishtime(86),
053
054 /** The path -and- filename of the file being transfered (typically fully qualified). For the receiver, SkypeKit sets this property upon acceptance of the incoming transfer. If not fully qualified, the path is assumed to be relative to the path of the SkypeKit runtime. <br>, type: String */
055 filepath(87),
056
057 /** The filename -only- of the file being transfered. The receiver side can use this property to pre-populate relevant UI components while prompting the user to accept the incoming transfer. <br>, type: String */
058 filename(88),
059
060 /** The size of the file being transferred in bytes. Depending on the magnitude of this value, your UI might want to display the size in terms of kilobytes or even megabytes. <br>, type: String */
061 filesize(89),
062
063 /** The number of bytes already transferred. Calculate the percentage of the file transferred so far as: <br>@code <br>P_BYTESTRANSFERRED / (P_FILESIZE / 100); <br></CODE> <br><br>Use float variables to avoid problems with files smaller than 100 bytes! <br>, type: String */
064 bytestransferred(90),
065
066 /** Current data transfer speed in bytes per second. Typically, your UI will want to display this value as kilobytes per second (KBps). <br>, type: int */
067 bytespersecond(91),
068
069 /** The "global ID" of this Transfer's associated Message instance. GUIDs are shared across Skype client instances and across all users that can see this Message. <br>, type: byte[] */
070 chatmsg_guid(92),
071
072 /** A more or less arbitrary index for ordering multiple file transfers within the UI. <br>, type: int */
073 chatmsg_index(93),
074
075 /** The "global ID" of this Transfer's associated Conversation (as chained through its associated Message). GUIDs are shared across Skype client instances and across all users that can see this Conversation. <br><br>Note that currently SkypeKit sets this property for INCOMING file transfers only and returns 0 (zero) for all sending side transfers. This is a known bug. <br>, type: Conversation */
076 convo_id(98);
077
078 private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
079
080 static {
081 for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
082 lookup.put(s.getId(), s);
083 }
084
085 private final int id;
086
087 private PROPERTY(int value) {
088 this.id = value;
089 }
090
091 public int getId() { return id; }
092
093 public static PROPERTY get(int code) {
094 return lookup.get(code);
095 }
096
097 public static PROPERTY fromString(String s) {
098 for (PROPERTY p : lookup.values()) {
099 if (p.toString() == s) {
100 return p;
101 }
102 }
103 return null;
104 }
105 }
106
107 public Object GetPropertyAsEnum(int propid) {
108 return PROPERTY.get(propid);
109 }
110
111 public String GetStrProperty(PROPERTY prop) {
112 //check in propcache if so then return
113 if (mPropCache.containsKey(new Integer(prop.id))){
114 String value = (String)mPropCache.get(prop.id);
115 if (value != null && !(value.length() == 0) ){
116 return value;
117 }
118 }
119 //else get from skypekit...
120 GetPropertyRequest request = new GetPropertyRequest(6, mObjectId, prop.id);
121
122 String string = null;
123 GetPropertyResponse r = skype.GetProperty(request);
124 if (r != null){
125 string = r.GetAsString();
126 }
127
128 if (string != null)
129 {
130 mPropCache.put(new Integer(prop.id), string);
131 }
132 return string;
133 }
134
135 public int GetIntProperty(PROPERTY prop) {
136 //check in propcache if so then return
137 if (mPropCache.containsKey(new Integer(prop.id))){
138 int value = ((Integer)mPropCache.get(prop.id)).intValue();
139 if (value != 0){
140 return value;
141 }
142 }
143 //else get from skypekit...
144 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
145
146 Integer integer = null;
147 GetPropertyResponse r = skype.GetProperty(request);
148 if (r != null){
149 integer = r.GetAsInt();
150 }
151
152 if (integer != null)
153 {
154 mPropCache.put(new Integer(prop.id), integer);
155 return integer.intValue();
156 }
157 else
158 {
159 return 0;
160 }
161 }
162
163 public boolean GetBooleanProperty(PROPERTY prop) {
164 //check in propcache if so then return
165 if (mPropCache.containsKey(new Integer(prop.id))){
166 return ((Boolean)mPropCache.get(prop.id)).booleanValue();
167 }
168 //else get from skypekit...
169 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
170
171 Boolean boolResp = null;
172 GetPropertyResponse r = skype.GetProperty(request);
173 if (r != null){
174 boolResp = r.GetAsBoolean();
175 }
176
177 if (boolResp != null)
178 {
179 mPropCache.put(new Integer(prop.id), boolResp);
180 return boolResp.booleanValue();
181 }
182 else
183 {
184 return false;
185 }
186 }
187
188 public byte [] GetBinProperty(PROPERTY prop) {
189 //get from skypekit...
190 GetPropertyRequest request = new GetPropertyRequest(6, mObjectId, prop.id);
191
192 byte [] data = null;
193 GetPropertyResponse r = skype.GetProperty(request);
194 if (r != null) {
195 data = r.GetAsBinary();
196 }
197 return data;
198 }
199
200 /**
201 */
202 public enum TYPE {
203
204 /** */
205 INCOMING(1),
206
207 /** */
208 OUTGOING(2);
209
210 private static final Map<Integer,TYPE> lookup = new HashMap<Integer,TYPE>();
211
212 static {
213 for(TYPE s : EnumSet.allOf(TYPE.class))
214 lookup.put(s.getId(), s);
215 }
216
217 private final int id;
218
219 private TYPE(int value) {
220 this.id = value;
221 }
222
223 public int getId() { return id; }
224
225 public static TYPE get(int code) {
226 return lookup.get(code);
227 }
228
229 public static TYPE fromString(String s) {
230 for (TYPE p : lookup.values()) {
231 if (p.toString() == s) {
232 return p;
233 }
234 }
235 return null;
236 }
237 }
238
239 /**
240 Recognized values for the P_STATUS property. Reflects current state of this Transfer. <br> */
241 public enum STATUS {
242
243 /** The file has either not been posted (sent) (OUTGOING), or not accepted (received) (INCOMING). <br>*/
244 NEW(0),
245
246 /** A temporary state that transitions either into TRANSFERRING (relayed or direct) or to FAILED. For unknown reasons, outgoing transfers tend go into this state twice - immediately before the actual data transfer starts and immediately after it ends. <br>*/
247 CONNECTING(1),
248
249 /** The files have been posted but the recipient has not yet accepted (or has declined) the transfer. <br>*/
250 WAITING_FOR_ACCEPT(2),
251
252 /** The transfer has been accepted and file data is being sent/received. Periodic updates of P_BYTESTRANSFERRED property should occur. <br>*/
253 TRANSFERRING(3),
254
255 /** The transfer has been accepted and file data is being sent/received but is going over at least one relay. Since relayed transfers tend to be significantly slower than direct transfers, you might want to differentiate the two in your UI and notify the user that relayed transfer typically take significantly longer to finish. <br>*/
256 TRANSFERRING_OVER_RELAY(4),
257
258 /** The local user (either the sender or a receiver) has paused the transfer. <br>*/
259 PAUSED(5),
260
261 /** A remote user has paused the transfer. For senders, a receiver has paused the transfer; for receivers, the sender has paused the transfer. <br>*/
262 REMOTELY_PAUSED(6),
263
264 /** Local side (either sender or receiver) has canceled the transfer. This is a final state of the STATE property. <br>*/
265 CANCELLED(7),
266
267 /** File transfer has completed. This is a terminal state. <br>*/
268 COMPLETED(8),
269
270 /** File transfer has failed. This is a terminal state. UI should provide feedback, based on value of P_FAILUREREASON. <br>*/
271 FAILED(9),
272
273 /** Transfer whose existence has been hinted by corresponding chat message, but which is yet to arrive. <br>*/
274 PLACEHOLDER(10),
275
276 /** Outgoing transfer object from another instance of the same account as current local login, running on another system. Hinted through chat message - only implies an offer was made; not necessarily accepted, failed, or completed. <br>*/
277 OFFER_FROM_OTHER_INSTANCE(11),
278
279 /** Remote side (either sender or receiver) has canceled the transfer. This is a final state of the STATE property. <br>*/
280 CANCELLED_BY_REMOTE(12);
281
282 private static final Map<Integer,STATUS> lookup = new HashMap<Integer,STATUS>();
283
284 static {
285 for(STATUS s : EnumSet.allOf(STATUS.class))
286 lookup.put(s.getId(), s);
287 }
288
289 private final int id;
290
291 private STATUS(int value) {
292 this.id = value;
293 }
294
295 public int getId() { return id; }
296
297 public static STATUS get(int code) {
298 return lookup.get(code);
299 }
300
301 public static STATUS fromString(String s) {
302 for (STATUS p : lookup.values()) {
303 if (p.toString() == s) {
304 return p;
305 }
306 }
307 return null;
308 }
309 }
310
311 /**
312 */
313 public enum FAILUREREASON {
314
315 /** */
316 SENDER_NOT_AUTHORISED(1),
317
318 /** */
319 REMOTELY_CANCELLED(2),
320
321 /** */
322 FAILED_READ(3),
323
324 /** */
325 FAILED_REMOTE_READ(4),
326
327 /** */
328 FAILED_WRITE(5),
329
330 /** */
331 FAILED_REMOTE_WRITE(6),
332
333 /** */
334 REMOTE_DOES_NOT_SUPPORT_FT(7),
335
336 /** */
337 REMOTE_OFFLINE_FOR_TOO_LONG(8),
338
339 /** */
340 TOO_MANY_PARALLEL(9),
341
342 /** */
343 PLACEHOLDER_TIMEOUT(10);
344
345 private static final Map<Integer,FAILUREREASON> lookup = new HashMap<Integer,FAILUREREASON>();
346
347 static {
348 for(FAILUREREASON s : EnumSet.allOf(FAILUREREASON.class))
349 lookup.put(s.getId(), s);
350 }
351
352 private final int id;
353
354 private FAILUREREASON(int value) {
355 this.id = value;
356 }
357
358 public int getId() { return id; }
359
360 public static FAILUREREASON get(int code) {
361 return lookup.get(code);
362 }
363
364 public static FAILUREREASON fromString(String s) {
365 for (FAILUREREASON p : lookup.values()) {
366 if (p.toString() == s) {
367 return p;
368 }
369 }
370 return null;
371 }
372 }
373
374 /**
375 *Accepts an incoming file transfer and saves it to specified file on the local file system. If the specified file exists, SkypeKit will silently overwrite it. Your UI should prompting the user for confirmation in this case and provide a means for canceling the file transfer or specifying a different target file. <br>
376 * @param filenameWithPath Where on the local file system to save the file being transferred. Note that you should specify the path as being fully-qualified. Otherwise, SkypeKit will be assume it to be relative to the SkypeKit runtime path, since the method is actually executed in the runtime context. <br>
377 * @return success Set to true if the specified target file was successfully created on the local file system -and- the initial write(s) succeeded. However, the transfer itself can subsequently fail before completion due to its being canceled (either locally or remotely), network failure, local file system space/write issues, and so forth. <br>
378 */
379 public boolean Accept( String filenameWithPath) {
380
381 Request request = null;
382 try {
383 request = new XCallRequest(6,3);
384 } catch (IOException e) {
385 e.printStackTrace();
386 if (skype.errorListener != null)
387 skype.errorListener.OnSkypeKitFatalError();
388 }
389 request.addParm('O',0,mObjectId);
390 request.addParm('f',1,filenameWithPath);
391
392 Response r = skype.XCall((XCallRequest)request);
393
394 if (r.isErrCall())
395 return false;
396
397 boolean success = false;
398 success = r.GetAsBoolean(1);
399 return success;
400 }
401
402 /**
403 *Temporarily pauses an in-progress incoming or outgoing file transfer. For incoming transfers, only this affects the sender and the invoking recipient only. For outgoing transfers, this affects the sender and all recipients. <br>
404 */
405 public void Pause() {
406
407 Request request = null;
408 try {
409 request = new XCallRequest(6,4);
410 } catch (IOException e) {
411 e.printStackTrace();
412 if (skype.errorListener != null)
413 skype.errorListener.OnSkypeKitFatalError();
414 }
415 request.addParm('O',0,mObjectId);
416
417 skype.XCall((XCallRequest)request);
418 }
419
420 /**
421 *Resumes a previously paused file transfer. <br>
422 */
423 public void Resume() {
424
425 Request request = null;
426 try {
427 request = new XCallRequest(6,5);
428 } catch (IOException e) {
429 e.printStackTrace();
430 if (skype.errorListener != null)
431 skype.errorListener.OnSkypeKitFatalError();
432 }
433 request.addParm('O',0,mObjectId);
434
435 skype.XCall((XCallRequest)request);
436 }
437
438 /**
439 *Cancels an in-progress file transfer. Transfer.STATUS will transition to CANCELLED for incoming file transfers and to CANCELLED_BY_REMOTE for outgoing transfers. <br>
440 */
441 public void Cancel() {
442
443 Request request = null;
444 try {
445 request = new XCallRequest(6,6);
446 } catch (IOException e) {
447 e.printStackTrace();
448 if (skype.errorListener != null)
449 skype.errorListener.OnSkypeKitFatalError();
450 }
451 request.addParm('O',0,mObjectId);
452
453 skype.XCall((XCallRequest)request);
454 }
455
456
457 }