aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/FileTransferManager.java
blob: 017b88eaeff3f3ecefa0efaa3a1183ce09e0ab11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package de.thedevstack.conversationsplus.services.filetransfer;

import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.services.FileTransferService;
import de.thedevstack.conversationsplus.services.filetransfer.httpupload.HttpFileTransferEntity;
import de.thedevstack.conversationsplus.utils.MessageUtil;

/**
 *
 */
public class FileTransferManager implements FileTransferStatusListener {
    private SortedSet<WeightedTransferService> transferServices;
    private static final FileTransferManager INSTANCE = new FileTransferManager();
    private final HashMap<String, WeightedTransferService> activeTransfers = new HashMap<>();

    private FileTransferManager() {
        this.transferServices = new TreeSet<>();
    }

    public static FileTransferManager getInstance() {
        return INSTANCE;
    }

    public static void init(FileTransferService... fileTransferServices) {
        if (null != fileTransferServices && fileTransferServices.length > 0) {
            for (FileTransferService fts : fileTransferServices) {
                addFileTransferService(fts);
            }
        }
    }

    public static void init(HashMap<Integer, FileTransferService> fileTransferServices) {
        for (Map.Entry<Integer, FileTransferService> entry : fileTransferServices.entrySet()) {
            addFileTransferService(entry.getKey(), entry.getValue());
        }
    }

    public static void addFileTransferService(int weight, FileTransferService fts) {
        fts.addFileTransferStatusListener(INSTANCE);
        INSTANCE.transferServices.add(new WeightedTransferService(weight, fts));
    }

    public static void addFileTransferService(FileTransferService fts) {
        int weight = 1;
        if (!INSTANCE.transferServices.isEmpty()) {
            weight = INSTANCE.transferServices.last().weight + 1;
        }
        addFileTransferService(weight, fts);
    }

    /**
     * Transfers a file for the corresponding message.
     *
     * @param message the message containing the file to transfer
     * @return <code>true</code> if the file transfer was successful, <code>false</code> otherwise
     */
    public boolean transferFile(Message message) {
        return this.transferFile(message, false);
    }

    /**
     * Transfers a file for the corresponding message.
     *
     * @param message the message containing the file to transfer
     * @param delay   whether the message is delayed or not
     * @return <code>true</code> if the file transfer was successful, <code>false</code> otherwise
     */
    public boolean transferFile(Message message, boolean delay) {
        Logging.d(Config.LOGTAG, "send file message");
        boolean transferSuccessfullyStarted = false;
        for (WeightedTransferService wts : this.transferServices) {
            try {
                if (wts.fileTransferService.accept(message)) {
                    transferSuccessfullyStarted = this.startFileTransfer(message, delay, wts);
                    if (transferSuccessfullyStarted) {
                        break;
                    }
                }
            } catch (Exception e) {
                //TODO Do real exception handling!!!!!
            }
        }
        return transferSuccessfullyStarted;
    }

    /**
     * Checks whether a message can be sent using this service or not.
     *
     * @param message the message to be checked
     * @return <code>true</code> if the message can be processed, <code>false</code> otherwise
     */
    public boolean accept(Message message) {
        return message.needsUploading();
    }

    @Override
    public void onFailure(FileTransferEntity entity, FileTransferFailureReason failureReason) {
        WeightedTransferService wts = this.activeTransfers.get(entity.getMessage().getUuid());
        if (null == wts) {
            return;
        }
        boolean delayed = (entity instanceof HttpFileTransferEntity) && ((HttpFileTransferEntity) entity).isDelayed();
        if (failureReason.isRecoverable()) {
            wts.fileTransferService.transferFile(entity.getMessage(), delayed);
        } else {
            boolean retransferStarted = false;
            this.activeTransfers.remove(entity.getMessage().getUuid());
            for (WeightedTransferService newWts : this.transferServices.tailSet(wts)) {
                if (newWts == wts) { // Same Reference
                    continue;
                }
                if (newWts.fileTransferService.accept(entity.getMessage())) {
                    retransferStarted = startFileTransfer(entity.getMessage(), delayed, newWts);
                    if (retransferStarted) {
                        break;
                    }
                }
            }
            if (!retransferStarted) {
                MessageUtil.markMessage(entity.getMessage(), Message.STATUS_SEND_FAILED);
            }
        }
    }

    @Override
    public void onCancel(FileTransferEntity entity) {
        this.activeTransfers.remove(entity.getMessage().getUuid());
        MessageUtil.markMessage(entity.getMessage(), Message.STATUS_SEND_FAILED); // TODO New Status CANCELED!
    }

    @Override
    public void onSuccess(FileTransferEntity entity) {
        this.activeTransfers.remove(entity.getMessage().getUuid());
    }

    private boolean startFileTransfer(Message message, boolean delayed, WeightedTransferService wts) {
        boolean transferSuccessfullyStarted = wts.fileTransferService.transferFile(message, delayed);
        if (transferSuccessfullyStarted) {
            this.activeTransfers.put(message.getUuid(), wts);
        }
        return transferSuccessfullyStarted;
    }

    static class WeightedTransferService implements Comparable<WeightedTransferService> {
        int weight;
        FileTransferService fileTransferService;

        WeightedTransferService(int weight, FileTransferService service) {
            this.weight = weight;
            this.fileTransferService = service;
        }

        /**
         * Compares this object to the specified object to determine their relative
         * order.
         *
         * @param another the object to compare to this instance.
         * @return a negative integer if this instance is less than {@code another};
         * a positive integer if this instance is greater than
         * {@code another}; 0 if this instance has the same order as
         * {@code another}.
         * @throws ClassCastException if {@code another} cannot be converted into something
         *                            comparable to {@code this} instance.
         */
        @Override
        public int compareTo(WeightedTransferService another) {
            return this.weight - another.weight;
        }
    }
}