    001    package com.skype.api;
    002
    003    import com.skype.ipc.SidRoot;
    004    import com.skype.ipc.SidObject;
    005    import com.skype.ipc.EnumConverting;
    006    import com.skype.ipc.PropertyEnumConverting;
    007    import com.skype.ipc.Decoding;
    008    import com.skype.ipc.Encoding;
    009    import com.skype.ipc.Encoding;
    010    import java.io.IOException;
    011    import com.skype.ipc.PropertyEnumConverting;
    012    import com.skype.ipc.SidGetResponding;
    013
    014    /**
    015     * 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).
    016     *
    017     * 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
    018     * this Message instance, and the Message.P_BODY_XML looks like this:
    019     *
    020     * @code
    021     * Some text<files alt=""><file size="2336020" index="0">test.zip</file></files>
    022     * </CODE>
    023     *
    024     * To put it another way, the object chain goes like this:
    025     * @code
    026     * Conversation->Message->Transfer
    027     * </CODE>
    028     *
    029     * 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:
    030     *  - file size in bytes
    031     *  - index - more or less arbitrary order in which the transfers should be displayed in the UI
    032     *  - file name.
    033     *
    034     * 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
    035     * a Conversation using the same Message, Message:GetTransfers retrieves a list of Transfer instances - one per file per recipient.
    036     *
    037     * You can determine the direction of particular Transfer instance by querying Transfer.P_TYPE (INCOMING/OUTGOING).
    038     *
    039     * 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.
    040     *
    041     * Another property of interest is Transfer.P_STATUS. The normal transfer status sequence during successful outgoing transfer is this:
    042     *  - TRANSFER STATUS -> NEW
    043     *  - TRANSFER STATUS -> WAITING_FOR_ACCEPT
    044     *  - TRANSFER STATUS -> CONNECTING
    045     *  - TRANSFER STATUS -> TRANSFERRING
    046     *  - TRANSFER STATUS -> CONNECTING
    047     *  - TRANSFER STATUS -> COMPLETED
    048     *
    049     * The list of all terminal Transfer statuses is:
    050     *  - COMPLETED
    051     *  - FAILED
    052     *  - CANCELLED
    053     *  - CANCELLED_BY_REMOTE
    054     *
    055     * 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.
    056     *
    057     * Incoming transfers, once accepted, overwrite existing files with the same name. Before accepting an incoming file transfer,
    058     * your UI should prompt the recipient to:
    059     *  - accept or decline the file
    060     *  - if accepted, specify the directory of here to save the file (with a pre-filled default)
    061     *  - 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
    062     *
    063     * Similarly, your UI should verify the existence of outgoing files prior to invoking Conversation.PostFiles.
    064     *
    065     * 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.
    066     */
    067    public final class Transfer extends SidObject {
    068            public enum Type implements EnumConverting {
    069                    INCOMING(1),
    070                    OUTGOING(2);
    071                    private final int key;
    072                    Type(int key) {
    073                            this.key = key;
    074                    };
    075                    public int getId()   { return key; }
    076                    public EnumConverting getDefault() { return INCOMING; }
    077                    public EnumConverting convert(int from) { return Type.get(from); }
    078                    public EnumConverting[] getArray(final int size) { return new Type[size]; }
    079                    public static Type get(int from) {
    080                            switch (from) {
    081                            case 1: return INCOMING;
    082                            case 2: return OUTGOING;
    083                            }
    084                            return INCOMING;
    085                    }
    086                    public static final int INCOMING_VALUE = 1;
    087                    public static final int OUTGOING_VALUE = 2;
    088            }
    089            /** Recognized values for the P_STATUS property. Reflects current state of this Transfer.  */
    090            public enum Status implements EnumConverting {
    091                    /** The file has either not been posted (sent) (OUTGOING), or not accepted (received) (INCOMING).  */
    092                    NEW                      (0),
    093                    /** 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.  */
    094                    CONNECTING               (1),
    095                    /** The files have been posted but the recipient has not yet accepted (or has declined) the transfer.  */
    096                    WAITING_FOR_ACCEPT       (2),
    097                    /** The transfer has been accepted and file data is being sent/received. Periodic updates of P_BYTESTRANSFERRED property should occur.  */
    098                    TRANSFERRING             (3),
    099                    /** 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.  */
    100                    TRANSFERRING_OVER_RELAY  (4),
    101                    /** The local user (either the sender or a receiver) has paused the transfer.  */
    102                    PAUSED                   (5),
    103                    /** A remote user has paused the transfer. For senders, a receiver has paused the transfer; for receivers, the sender has paused the transfer.  */
    104                    REMOTELY_PAUSED          (6),
    105                    /** Local side (either sender or receiver) has canceled the transfer. This is a final state of the STATE property.  */
    106                    CANCELLED                (7),
    107                    /** File transfer has completed. This is a terminal state.  */
    108                    COMPLETED                (8),
    109                    /** File transfer has failed. This is a terminal state. UI should provide feedback, based on value of P_FAILUREREASON.  */
    110                    FAILED                   (9),
    111                    /** Transfer whose existence has been hinted by corresponding chat message, but which is yet to arrive.  */
    112                    PLACEHOLDER              (10),
    113                    /** 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.   */
    114                    OFFER_FROM_OTHER_INSTANCE(11),
    115                    /** Remote side (either sender or receiver) has canceled the transfer. This is a final state of the STATE property.  */
    116                    CANCELLED_BY_REMOTE      (12);
    117                    private final int key;
    118                    Status(int key) {
    119                            this.key = key;
    120                    };
    121                    public int getId()   { return key; }
    122                    public EnumConverting getDefault() { return NEW; }
    123                    public EnumConverting convert(int from) { return Status.get(from); }
    124                    public EnumConverting[] getArray(final int size) { return new Status[size]; }
    125                    public static Status get(int from) {
    126                            switch (from) {
    127                            case  0: return NEW;
    128                            case  1: return CONNECTING;
    129                            case  2: return WAITING_FOR_ACCEPT;
    130                            case  3: return TRANSFERRING;
    131                            case  4: return TRANSFERRING_OVER_RELAY;
    132                            case  5: return PAUSED;                 case  6: return REMOTELY_PAUSED;
    133                            case  7: return CANCELLED;
    134                            case  8: return COMPLETED;
    135                            case  9: return FAILED;
    136                            case 10: return PLACEHOLDER;
    137                            case 11: return OFFER_FROM_OTHER_INSTANCE;
    138                            case 12: return CANCELLED_BY_REMOTE;
    139                            }
    140                            return NEW;
    141                    }
    142                    public static final int NEW_VALUE                       =  0;
    143                    public static final int CONNECTING_VALUE                =  1;
    144                    public static final int WAITING_FOR_ACCEPT_VALUE        =  2;
    145                    public static final int TRANSFERRING_VALUE              =  3;
    146                    public static final int TRANSFERRING_OVER_RELAY_VALUE   =  4;
    147                    public static final int PAUSED_VALUE                    =  5;
    148                    public static final int REMOTELY_PAUSED_VALUE           =  6;
    149                    public static final int CANCELLED_VALUE                 =  7;
    150                    public static final int COMPLETED_VALUE                 =  8;
    151                    public static final int FAILED_VALUE                    =  9;
    152                    public static final int PLACEHOLDER_VALUE               = 10;
    153                    public static final int OFFER_FROM_OTHER_INSTANCE_VALUE = 11;
    154                    public static final int CANCELLED_BY_REMOTE_VALUE       = 12;
    155            }
    156            public enum FailureReason implements EnumConverting {
    157                    SENDER_NOT_AUTHORISED      (1),
    158                    REMOTELY_CANCELLED         (2),
    159                    FAILED_READ                (3),
    160                    FAILED_REMOTE_READ         (4),
    161                    FAILED_WRITE               (5),
    162                    FAILED_REMOTE_WRITE        (6),
    163                    REMOTE_DOES_NOT_SUPPORT_FT (7),
    164                    REMOTE_OFFLINE_FOR_TOO_LONG(8),
    165                    TOO_MANY_PARALLEL          (9),
    166                    PLACEHOLDER_TIMEOUT        (10);
    167                    private final int key;
    168                    FailureReason(int key) {
    169                            this.key = key;
    170                    };
    171                    public int getId()   { return key; }
    172                    public EnumConverting getDefault() { return SENDER_NOT_AUTHORISED; }
    173                    public EnumConverting convert(int from) { return FailureReason.get(from); }
    174                    public EnumConverting[] getArray(final int size) { return new FailureReason[size]; }
    175                    public static FailureReason get(int from) {
    176                            switch (from) {
    177                            case  1: return SENDER_NOT_AUTHORISED;
    178                            case  2: return REMOTELY_CANCELLED;
    179                            case  3: return FAILED_READ;
    180                            case  4: return FAILED_REMOTE_READ;
    181                            case  5: return FAILED_WRITE;
    182                            case  6: return FAILED_REMOTE_WRITE;
    183                            case  7: return REMOTE_DOES_NOT_SUPPORT_FT;
    184                            case  8: return REMOTE_OFFLINE_FOR_TOO_LONG;
    185                            case  9: return TOO_MANY_PARALLEL;
    186                            case 10: return PLACEHOLDER_TIMEOUT;
    187                            }
    188                            return SENDER_NOT_AUTHORISED;
    189                    }
    190                    public static final int SENDER_NOT_AUTHORISED_VALUE       =  1;
    191                    public static final int REMOTELY_CANCELLED_VALUE          =  2;
    192                    public static final int FAILED_READ_VALUE                 =  3;
    193                    public static final int FAILED_REMOTE_READ_VALUE          =  4;
    194                    public static final int FAILED_WRITE_VALUE                =  5;
    195                    public static final int FAILED_REMOTE_WRITE_VALUE         =  6;
    196                    public static final int REMOTE_DOES_NOT_SUPPORT_FT_VALUE  =  7;
    197                    public static final int REMOTE_OFFLINE_FOR_TOO_LONG_VALUE =  8;
    198                    public static final int TOO_MANY_PARALLEL_VALUE           =  9;
    199                    public static final int PLACEHOLDER_TIMEOUT_VALUE         = 10;
    200            }
    201            private final static byte[] P_TYPE_req = {(byte) 90,(byte) 71,(byte) 80,(byte) 93,(byte) 6};
    202            private final static byte[] P_PARTNER_HANDLE_req = {(byte) 90,(byte) 71,(byte) 81,(byte) 93,(byte) 6};
    203            private final static byte[] P_PARTNER_DISPLAY_NAME_req = {(byte) 90,(byte) 71,(byte) 82,(byte) 93,(byte) 6};
    204            private final static byte[] P_STATUS_req = {(byte) 90,(byte) 71,(byte) 83,(byte) 93,(byte) 6};
    205            private final static byte[] P_FAILURE_REASON_req = {(byte) 90,(byte) 71,(byte) 84,(byte) 93,(byte) 6};
    206            private final static byte[] P_START_TIME_req = {(byte) 90,(byte) 71,(byte) 85,(byte) 93,(byte) 6};
    207            private final static byte[] P_FINISH_TIME_req = {(byte) 90,(byte) 71,(byte) 86,(byte) 93,(byte) 6};
    208            private final static byte[] P_FILE_PATH_req = {(byte) 90,(byte) 71,(byte) 87,(byte) 93,(byte) 6};
    209            private final static byte[] P_FILE_NAME_req = {(byte) 90,(byte) 71,(byte) 88,(byte) 93,(byte) 6};
    210            private final static byte[] P_FILE_SIZE_req = {(byte) 90,(byte) 71,(byte) 89,(byte) 93,(byte) 6};
    211            private final static byte[] P_BYTES_TRANSFERRED_req = {(byte) 90,(byte) 71,(byte) 90,(byte) 93,(byte) 6};
    212            private final static byte[] P_BYTES_PER_SECOND_req = {(byte) 90,(byte) 71,(byte) 91,(byte) 93,(byte) 6};
    213            private final static byte[] P_CHAT_MSG_GUID_req = {(byte) 90,(byte) 71,(byte) 92,(byte) 93,(byte) 6};
    214            private final static byte[] P_CHAT_MSG_INDEX_req = {(byte) 90,(byte) 71,(byte) 93,(byte) 93,(byte) 6};
    215            private final static byte[] P_CONVERSATION_req = {(byte) 90,(byte) 71,(byte) 98,(byte) 93,(byte) 6};
    216            /** Properties of the Transfer class */
    217            public enum Property implements PropertyEnumConverting {
    218                    P_UNKNOWN             (0,0,null,0,null),
    219                    P_TYPE                (80, 1, P_TYPE_req, 0, Type.get(0)),
    220                    P_PARTNER_HANDLE      (81, 2, P_PARTNER_HANDLE_req, 0, null),
    221                    P_PARTNER_DISPLAY_NAME(82, 3, P_PARTNER_DISPLAY_NAME_req, 0, null),
    222                    P_STATUS              (83, 4, P_STATUS_req, 0, Status.get(0)),
    223                    P_FAILURE_REASON      (84, 5, P_FAILURE_REASON_req, 0, FailureReason.get(0)),
    224                    P_START_TIME          (85, 6, P_START_TIME_req, 0, null),
    225                    P_FINISH_TIME         (86, 7, P_FINISH_TIME_req, 0, null),
    226                    P_FILE_PATH           (87, 8, P_FILE_PATH_req, 0, null),
    227                    P_FILE_NAME           (88, 9, P_FILE_NAME_req, 0, null),
    228                    P_FILE_SIZE           (89, 10, P_FILE_SIZE_req, 0, null),
    229                    P_BYTES_TRANSFERRED   (90, 11, P_BYTES_TRANSFERRED_req, 0, null),
    230                    P_BYTES_PER_SECOND    (91, 12, P_BYTES_PER_SECOND_req, 0, null),
    231                    P_CHAT_MSG_GUID       (92, 13, P_CHAT_MSG_GUID_req, 0, null),
    232                    P_CHAT_MSG_INDEX      (93, 14, P_CHAT_MSG_INDEX_req, 0, null),
    233                    P_CONVERSATION        (98, 15, P_CONVERSATION_req, 18, null);
    234                    private final int    key;
    235                    private final int    idx;
    236                    private final byte[] req;
    237                    private final int    mod;
    238                    private final EnumConverting enumConverter;
    239                    Property(int key, int idx, byte[] req, int mod, EnumConverting converter) {
    240                            this.key = key;
    241                            this.idx = idx;
    242                            this.req = req;
    243                            this.mod = mod;
    244                            this.enumConverter = converter;
    245                    };
    246                    public boolean  isCached()    { return idx > 0;   }
    247                    public int      getIdx()      { return idx;       }
    248                    public int      getId()       { return key;       }
    249                    public byte[]   getRequest()  { return req;       }
    250                    public EnumConverting getDefault()  { return P_UNKNOWN; }
    251                    public int      getModuleId() { return mod;       }
    252                    public EnumConverting getEnumConverter()    { return enumConverter;   }
    253                    public EnumConverting convert(final int from) { return Property.get(from); }
    254                    public EnumConverting[] getArray(final int size) { return new Property[size]; }
    255                    public static Property get(final int from) {
    256                            switch (from) {
    257                            case 80: return P_TYPE;
    258                            case 81: return P_PARTNER_HANDLE;
    259                            case 82: return P_PARTNER_DISPLAY_NAME;
    260                            case 83: return P_STATUS;
    261                            case 84: return P_FAILURE_REASON;
    262                            case 85: return P_START_TIME;
    263                            case 86: return P_FINISH_TIME;
    264                            case 87: return P_FILE_PATH;
    265                            case 88: return P_FILE_NAME;
    266                            case 89: return P_FILE_SIZE;
    267                            case 90: return P_BYTES_TRANSFERRED;
    268                            case 91: return P_BYTES_PER_SECOND;
    269                            case 92: return P_CHAT_MSG_GUID;
    270                            case 93: return P_CHAT_MSG_INDEX;
    271                            case 98: return P_CONVERSATION;
    272                            }
    273                            return P_UNKNOWN;
    274                    }
    275                    public static final int P_TYPE_VALUE                 = 80;
    276                    public static final int P_PARTNER_HANDLE_VALUE       = 81;
    277                    public static final int P_PARTNER_DISPLAY_NAME_VALUE = 82;
    278                    public static final int P_STATUS_VALUE               = 83;
    279                    public static final int P_FAILURE_REASON_VALUE       = 84;
    280                    public static final int P_START_TIME_VALUE           = 85;
    281                    public static final int P_FINISH_TIME_VALUE          = 86;
    282                    public static final int P_FILE_PATH_VALUE            = 87;
    283                    public static final int P_FILE_NAME_VALUE            = 88;
    284                    public static final int P_FILE_SIZE_VALUE            = 89;
    285                    public static final int P_BYTES_TRANSFERRED_VALUE    = 90;
    286                    public static final int P_BYTES_PER_SECOND_VALUE     = 91;
    287                    public static final int P_CHAT_MSG_GUID_VALUE        = 92;
    288                    public static final int P_CHAT_MSG_INDEX_VALUE       = 93;
    289                    public static final int P_CONVERSATION_VALUE         = 98;
    290            }
    291            private final static byte[] accept_req = {(byte) 90,(byte) 82,(byte) 6,(byte) 3};
    292            /** 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.  * @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.      * @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.
    293             */
    294            public boolean accept(String filenameWithPath) {
    295                    try {
    296                            return sidDoRequest(accept_req)
    297                            .addFilenameParm(1, filenameWithPath)
    298                            .endRequest().getBoolParm(1, true);
    299                    } catch(IOException e) {
    300                            mSidRoot.sidOnFatalError(e);
    301                            return false
    302                    ;}
    303            }
    304            private final static byte[] pause_req = {(byte) 90,(byte) 82,(byte) 6,(byte) 4};
    305            /** 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.  */
    306            public void pause() {
    307                    try {
    308                            sidDoRequest(pause_req)
    309                            .endOneWay();
    310                    } catch(IOException e) {
    311                            mSidRoot.sidOnFatalError(e);
    312                    }
    313            }
    314            private final static byte[] resume_req = {(byte) 90,(byte) 82,(byte) 6,(byte) 5};
    315            /** Resumes a previously paused file transfer.  */
    316            public void resume() {
    317                    try {
    318                            sidDoRequest(resume_req)
    319                            .endOneWay();
    320                    } catch(IOException e) {
    321                            mSidRoot.sidOnFatalError(e);
    322                    }
    323            }
    324            private final static byte[] cancel_req = {(byte) 90,(byte) 82,(byte) 6,(byte) 6};
    325            /** Cancels an in-progress file transfer. Transfer.STATUS will transition to CANCELLED for incoming file transfers and to CANCELLED_BY_REMOTE for outgoing transfers.  */
    326            public void cancel() {
    327                    try {
    328                            sidDoRequest(cancel_req)
    329                            .endOneWay();
    330                    } catch(IOException e) {
    331                            mSidRoot.sidOnFatalError(e);
    332                    }
    333            }
    334            /***
    335             * generic multiget of a list of Property
    336             * @param requested the list of requested properties of Transfer
    337             * @return SidGetResponding
    338             */
    339            public SidGetResponding sidMultiGet(Property[] requested) {
    340                    return super.sidMultiGet(requested);
    341            }
    342            /***
    343             * generic multiget of list of Property for a list of Transfer
    344             * @param requested the list of requested properties
    345             * @return SidGetResponding[] can be casted to (Transfer[]) if all properties are cached
    346             */
    347            static public SidGetResponding[] sidMultiGet(Property[] requested, Transfer[] objects) {
    348                    return SidObject.sidMultiGet(requested, objects);
    349            }
    350            /** INCOMING / OUTGOING  */
    351            public Type getType() {
    352                    synchronized(this) {
    353                            if ((mSidCached & 0x1) != 0)
    354                                    return mType;
    355                    }
    356                    return (Type) sidRequestEnumProperty(Property.P_TYPE);
    357            }
    358            /** 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.  */
    359            public String getPartnerHandle() {
    360                    synchronized(this) {
    361                            if ((mSidCached & 0x2) != 0)
    362                                    return mPartnerHandle;
    363                    }
    364                    return sidRequestStringProperty(Property.P_PARTNER_HANDLE);
    365            }
    366            /** Display name of the remote participant.  */
    367            public String getPartnerDisplayName() {
    368                    synchronized(this) {
    369                            if ((mSidCached & 0x4) != 0)
    370                                    return mPartnerDisplayName;
    371                    }
    372                    return sidRequestStringProperty(Property.P_PARTNER_DISPLAY_NAME);
    373            }
    374            /** Current state of the transfer  */
    375            public Status getStatus() {
    376                    synchronized(this) {
    377                            if ((mSidCached & 0x8) != 0)
    378                                    return mStatus;
    379                    }
    380                    return (Status) sidRequestEnumProperty(Property.P_STATUS);
    381            }
    382            /** Set whenever P_STATUS transitions to FAILED.  */
    383            public FailureReason getFailureReason() {
    384                    synchronized(this) {
    385                            if ((mSidCached & 0x10) != 0)
    386                                    return mFailureReason;
    387                    }
    388                    return (FailureReason) sidRequestEnumProperty(Property.P_FAILURE_REASON);
    389            }
    390            /** 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.  */
    391            public int getStartTime() {
    392                    synchronized(this) {
    393                            if ((mSidCached & 0x20) != 0)
    394                                    return mStartTime;
    395                    }
    396                    return sidRequestUintProperty(Property.P_START_TIME);
    397            }
    398            /** UNIX timestamp of when this Transfer COMPLETED or FAILED. This property is never set if the receiving side (local or remote) canceled the transfer.  */
    399            public int getFinishTime() {
    400                    synchronized(this) {
    401                            if ((mSidCached & 0x40) != 0)
    402                                    return mFinishTime;
    403                    }
    404                    return sidRequestUintProperty(Property.P_FINISH_TIME);
    405            }
    406            /** 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.  */
    407            public String getFilePath() {
    408                    synchronized(this) {
    409                            if ((mSidCached & 0x80) != 0)
    410                                    return mFilePath;
    411                    }
    412                    return sidRequestStringProperty(Property.P_FILE_PATH);
    413            }
    414            /** 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.  */
    415            public String getFileName() {
    416                    synchronized(this) {
    417                            if ((mSidCached & 0x100) != 0)
    418                                    return mFileName;
    419                    }
    420                    return sidRequestStringProperty(Property.P_FILE_NAME);
    421            }
    422            /** 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.  */
    423            public String getFileSize() {
    424                    synchronized(this) {
    425                            if ((mSidCached & 0x200) != 0)
    426                                    return mFileSize;
    427                    }
    428                    return sidRequestStringProperty(Property.P_FILE_SIZE);
    429            }
    430            /**
    431             * The number of bytes already transferred. Calculate the percentage of the file transferred so far as:
    432             * @code
    433             * P_BYTESTRANSFERRED / (P_FILESIZE / 100);
    434             * </CODE>
    435             *
    436             * Use float variables to avoid problems with files smaller than 100 bytes!
    437             */
    438            public String getBytesTransferred() {
    439                    synchronized(this) {
    440                            if ((mSidCached & 0x400) != 0)
    441                                    return mBytesTransferred;
    442                    }
    443                    return sidRequestStringProperty(Property.P_BYTES_TRANSFERRED);
    444            }
    445            /** Current data transfer speed in bytes per second. Typically, your UI will want to display this value as kilobytes per second (KBps).  */
    446            public int getBytesPerSecond() {
    447                    synchronized(this) {
    448                            if ((mSidCached & 0x800) != 0)
    449                                    return mBytesPerSecond;
    450                    }
    451                    return sidRequestUintProperty(Property.P_BYTES_PER_SECOND);
    452            }
    453            /** 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.  */
    454            public byte[] getChatMsgGuid() {
    455                    synchronized(this) {
    456                            if ((mSidCached & 0x1000) != 0)
    457                                    return mChatMsgGuid;
    458                    }
    459                    return sidRequestBinaryProperty(Property.P_CHAT_MSG_GUID);
    460            }
    461            /** A more or less arbitrary index for ordering multiple file transfers within the UI.  */
    462            public int getChatMsgIndex() {
    463                    synchronized(this) {
    464                            if ((mSidCached & 0x2000) != 0)
    465                                    return mChatMsgIndex;
    466                    }
    467                    return sidRequestUintProperty(Property.P_CHAT_MSG_INDEX);
    468            }
    469            /**
    470             * 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.
    471             *
    472             * 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.
    473             */
    474            public Conversation getConversation() {
    475                    synchronized(this) {
    476                            if ((mSidCached & 0x4000) != 0)
    477                                    return mConversation;
    478                    }
    479                    return (Conversation) sidRequestObjectProperty(Property.P_CONVERSATION);
    480            }
    481            public String sidGetStringProperty(final PropertyEnumConverting prop) {
    482                    switch(prop.getId()) {
    483                    case 81:
    484                            return mPartnerHandle;
    485                    case 82:
    486                            return mPartnerDisplayName;
    487                    case 87:
    488                            return mFilePath;
    489                    case 88:
    490                            return mFileName;
    491                    case 89:
    492                            return mFileSize;
    493                    case 90:
    494                            return mBytesTransferred;
    495                    }
    496                    return "";
    497            }
    498            public SidObject sidGetObjectProperty(final PropertyEnumConverting prop) {
    499                    assert(prop.getId() == 98);
    500                    return mConversation;
    501            }
    502            public int sidGetIntProperty(final PropertyEnumConverting prop) {
    503                    switch(prop.getId()) {          case 85:
    504                            return mStartTime;
    505                    case 86:
    506                            return mFinishTime;
    507                    case 91:
    508                            return mBytesPerSecond;
    509                    case 93:
    510                            return mChatMsgIndex;
    511                    }
    512                    return 0;
    513            }
    514            public EnumConverting sidGetEnumProperty(final PropertyEnumConverting prop) {
    515                    switch(prop.getId()) {
    516                    case 80:
    517                            return mType;
    518                    case 83:
    519                            return mStatus;
    520                    case 84:
    521                            return mFailureReason;
    522                    }
    523                    return null;
    524            }
    525            public byte[] sidGetBinaryProperty(final PropertyEnumConverting prop) {
    526                    assert(prop.getId() == 92);
    527                    return mChatMsgGuid;
    528            }
    529            public String getPropertyAsString(final int prop) {
    530                    switch (prop) {
    531                    case 80: return getType().toString();
    532                    case 81: return getPartnerHandle();
    533                    case 82: return getPartnerDisplayName();
    534                    case 83: return getStatus().toString();
    535                    case 84: return getFailureReason().toString();
    536                    case 85: return Integer.toString(getStartTime());
    537                    case 86: return Integer.toString(getFinishTime());
    538                    case 87: return getFilePath();
    539                    case 88: return getFileName();
    540                    case 89: return getFileSize();
    541                    case 90: return getBytesTransferred();
    542                    case 91: return Integer.toString(getBytesPerSecond());
    543                    case 92: return "<binary>";
    544                    case 93: return Integer.toString(getChatMsgIndex());
    545                    case 98: return Integer.toString(getConversation().getOid());
    546                    }
    547                    return "<unkown>";
    548            }
    549            public String getPropertyAsString(final Property prop) {
    550                    return getPropertyAsString(prop.getId());
    551            }
    552            protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue) {
    553                    final Property property = Property.get(propertyId);
    554                    if (property == Property.P_UNKNOWN) return;
    555                    final int idx = property.getIdx();
    556                    if (idx != 0) {
    557                            int bit  = 1<<((idx-1)%32);
    558                            synchronized (this) {
    559                                    mSidCached|=bit;
    560                                    switch (propertyId) {
    561                                    case 80: mType = Type.get(value); break;
    562                                    case 81:
    563                                            if (svalue != null) mPartnerHandle = svalue;
    564                                            else mSidCached &=~bit;
    565                                            break;
    566                                    case 82:
    567                                            if (svalue != null) mPartnerDisplayName = svalue;
    568                                            else mSidCached &=~bit;
    569                                            break;
    570                                    case 83: mStatus = Status.get(value); break;
    571                                    case 84: mFailureReason = FailureReason.get(value); break;
    572                                    case 85: mStartTime = value; break;
    573                                    case 86: mFinishTime = value; break;
    574                                    case 87:
    575                                            if (svalue != null) mFilePath = svalue;
    576                                            else mSidCached &=~bit;
    577                                            break;
    578                                    case 88:
    579                                            if (svalue != null) mFileName = svalue;
    580                                            else mSidCached &=~bit;
    581                                            break;
    582                                    case 89:
    583                                            if (svalue != null) mFileSize = svalue;
    584                                            else mSidCached &=~bit;
    585                                            break;
    586                                    case 90:
    587                                            if (svalue != null) mBytesTransferred = svalue;
    588                                            else mSidCached &=~bit;
    589                                            break;
    590                                    case 91: mBytesPerSecond = value; break;
    591                                    case 93: mChatMsgIndex = value; break;
    592                                    default: mSidCached|=bit; break;
    593                                    }
    594                            }
    595                    }
    596                    TransferListener listener = ((Skype) mSidRoot).getTransferListener();
    597                    if (listener != null)
    598                            listener.onPropertyChange(this, property, value, svalue);
    599            }
    600            public void sidSetProperty(final PropertyEnumConverting prop, final String newValue) {
    601                    final int propId = prop.getId();
    602                    switch(propId) {
    603                    case 81:
    604                            mSidCached |= 0x2;
    605                            mPartnerHandle=  newValue;
    606                            break;
    607                    case 82:
    608                            mSidCached |= 0x4;
    609                            mPartnerDisplayName=  newValue;
    610                            break;
    611                    case 87:
    612                            mSidCached |= 0x80;
    613                            mFilePath=  newValue;
    614                            break;
    615                    case 88:
    616                            mSidCached |= 0x100;
    617                            mFileName=  newValue;
    618                            break;
    619                    case 89:
    620                            mSidCached |= 0x200;
    621                            mFileSize=  newValue;
    622                            break;
    623                    case 90:
    624                            mSidCached |= 0x400;
    625                            mBytesTransferred=  newValue;
    626                            break;
    627                    }
    628            }
    629            public void sidSetProperty(final PropertyEnumConverting prop, final SidObject newValue) {
    630                    final int propId = prop.getId();
    631                    assert(propId == 98);
    632                    mSidCached |= 0x4000;
    633                    mConversation= (Conversation) newValue;
    634            }
    635            public void sidSetProperty(final PropertyEnumConverting prop, final int newValue) {
    636                    final int propId = prop.getId();
    637                    switch(propId) {
    638                    case 80:
    639                            mSidCached |= 0x1;
    640                            mType= Type.get(newValue);
    641                            break;
    642                    case 83:
    643                            mSidCached |= 0x8;
    644                            mStatus= Status.get(newValue);
    645                            break;
    646                    case 84:
    647                            mSidCached |= 0x10;
    648                            mFailureReason= FailureReason.get(newValue);
    649                            break;
    650                    case 85:
    651                            mSidCached |= 0x20;
    652                            mStartTime=  newValue;
    653                            break;
    654                    case 86:
    655                            mSidCached |= 0x40;
    656                            mFinishTime=  newValue;
    657                            break;
    658                    case 91:
    659                            mSidCached |= 0x800;
    660                            mBytesPerSecond=  newValue;
    661                            break;
    662                    case 93:
    663                            mSidCached |= 0x2000;
    664                            mChatMsgIndex=  newValue;
    665                            break;
    666                    }
    667            }
    668            public void sidSetProperty(final PropertyEnumConverting prop, final byte[] newValue) {
    669                    final int propId = prop.getId();
    670                    assert(propId == 92);
    671                    mSidCached |= 0x1000;
    672                    mChatMsgGuid=  newValue;
    673            }
    674            public Type          mType;
    675            public String        mPartnerHandle;
    676            public String        mPartnerDisplayName;
    677            public Status        mStatus;
    678            public FailureReason mFailureReason;
    679            public int           mStartTime;
    680            public int           mFinishTime;
    681            public String        mFilePath;
    682            public String        mFileName;
    683            public String        mFileSize;
    684            public String        mBytesTransferred;
    685            public int           mBytesPerSecond;
    686            public byte[]        mChatMsgGuid;
    687            public int           mChatMsgIndex;
    688            public Conversation  mConversation;
    689            public int moduleId() {
    690                    return 6;
    691            }
    692
    693            public Transfer(final int oid, final SidRoot root) {
    694                    super(oid, root, 15);
    695            }
    696    }