package de.thedevstack.conversationsplus.services.filetransfer; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import de.thedevstack.conversationsplus.entities.DownloadableFile; import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.entities.Transferable; import de.thedevstack.conversationsplus.persistance.FileBackend; import de.thedevstack.conversationsplus.utils.StreamUtil; /** * */ public class FileTransferEntity implements Transferable { /** * Listeners to inform about a status change. */ private final List statusListeners = new ArrayList<>(); /** * The associated message. */ private final Message message; /** * Number of attempts. */ private int attempts = 0; /** * The status of the file transfer. */ private FileTransferStatusEnum transferStatus; /** * Number of bytes transmitted. */ private long transmitted = 0; /** * The associated file. */ private DownloadableFile file; /** * The associated file as stream. */ private InputStream fileInputStream; /** * Initializes the FileTransferEntity based on the associated message. * This initialization includes loading the file and associating this transferable to the message. * @param message the message in which the file to transfer is contained. */ public FileTransferEntity(Message message) { this.message = message; this.message.setTransferable(this); this.file = FileBackend.getFile(message, false); } /** * Start something. * Empty implementation since documentation in interface is missing. * @return false */ @Override public boolean start() { return false; } /** * Returns the global transferable status. * * @return {@value STATUS_FAILED} if #isFailed returns true, {@value STATUS_UPLOADING} otherwise */ @Override public int getStatus() { int status = (isFailed()) ? STATUS_FAILED : STATUS_UPLOADING; return status; } /** * Returns the expected file size of the underlying file. * @return the expected file size or 0 if no file is associated. */ @Override public long getFileSize() { return file == null ? 0 : file.getExpectedSize(); } /** * Calculates the current progress in percent. * * @return the current progress in percent */ @Override public int getProgress() { if (file == null) { return 0; } return (int) ((((double) transmitted) / file.getExpectedSize()) * 100); } /** * Cancels the file transfer and informs the listeners about cancellation. */ @Override public void cancel() { this.transferStatus = FileTransferStatusEnum.CANCELED; this.close(); for (FileTransferStatusListener listener : this.statusListeners) { listener.onCancel(this); } } /** * Starts an transfer attempt. */ public void startAttempt() { this.attempts++; this.transferStatus = FileTransferStatusEnum.TRANSFERRING; } /** * Fails an file transfer and informs the listeners about failure. * * @param failureReason the reason of failure. */ public void fail(FileTransferFailureReason failureReason) { this.transferStatus = FileTransferStatusEnum.FAILED; this.close(); failureReason.setAttempt(this.attempts); for (FileTransferStatusListener listener : this.statusListeners) { listener.onFailure(this, failureReason); } } /** * Updates the progress by adding parameter value to current progress value. * * @param progress the number of newly transferred bytes */ public void updateProgress(long progress) { if (0 == this.attempts) { this.startAttempt(); } this.transmitted += progress; } /** * Set the status of the file transfer to FileTransferStatusEnum#TRANSFERRED and informs the listeners about success. */ public void transferred() { this.transferStatus = FileTransferStatusEnum.TRANSFERRED; this.close(); for (FileTransferStatusListener listener : this.statusListeners) { listener.onSuccess(this); } } /** * Closes the file input stream (if it is not yet closed) and removes association with message. */ private void close() { StreamUtil.close(this.fileInputStream); this.getMessage().setTransferable(null); } /** * Whether the file is transferred or not. * @return true if the file is successfully transferred, false otherwise */ public boolean isTransferred() { return FileTransferStatusEnum.TRANSFERRED == this.transferStatus; } /** * Whether the file transfer is canceled or not. * @return true if the file transfer was canceled, false otherwise */ public boolean isCanceled() { return FileTransferStatusEnum.CANCELED == this.transferStatus; } /** * Whether the file transfer failed or not. * @return true if the file transfer failed, false otherwise */ public boolean isFailed() { return FileTransferStatusEnum.FAILED == this.transferStatus; } public void setFileInputStream(InputStream fileInputStream) { this.fileInputStream = fileInputStream; } public InputStream getFileInputStream() { return fileInputStream; } public Message getMessage() { return message; } public DownloadableFile getFile() { return file; } public void addFileTransferStatusListener(FileTransferStatusListener... listeners) { this.statusListeners.addAll(Arrays.asList(listeners)); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FileTransferEntity that = (FileTransferEntity) o; String uuid = message != null ? message.getUuid() : null; String thatUuid = that.message != null ? that.message.getUuid() : null; return uuid != null ? uuid.equals(thatUuid) : thatUuid == null; } @Override public int hashCode() { return message != null && message.getUuid() != null ? message.getUuid().hashCode() : 0; } }