diff options
Diffstat (limited to '')
5 files changed, 348 insertions, 0 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/AutomaticFileDownload.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/AutomaticFileDownload.java new file mode 100644 index 00000000..5885e2e1 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/AutomaticFileDownload.java @@ -0,0 +1,61 @@ +package de.thedevstack.conversationsplus.services.filetransfer.http.download; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.http.HttpHeadRetrievedListener; +import de.thedevstack.conversationsplus.services.filetransfer.FileTransferManager; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + +/** + * + */ +public class AutomaticFileDownload implements HttpHeadRetrievedListener { + private boolean notify; + + public AutomaticFileDownload(boolean notify) { + this.notify = notify; + } + + @Override + public void onFileSizeRetrieved(long size, Message message) { + if (!this.transferFile(message)) { + + } + if (this.notify) { + XmppConnectionServiceAccessor.xmppConnectionService.getNotificationService().push(message); + } + } + + /** + * 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) { + if (this.accept(message)) { + Logging.d("http-download", "Starting automatic download"); + FileTransferManager ftm = FileTransferManager.getInstance(); + if (ftm.accept(message)) { + return ftm.transferFile(message); + } + } + return false; + } + + /** + * 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) { + long size = message.getFileParams().getSize(); + return ConversationsPlusApplication.hasStoragePermission() + && size > -1 + && size <= ConversationsPlusPreferences.autoAcceptFileSize() + && XmppConnectionServiceAccessor.xmppConnectionService.isDownloadAllowedInConnection(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferEntity.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferEntity.java new file mode 100644 index 00000000..fcd3904b --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferEntity.java @@ -0,0 +1,53 @@ +package de.thedevstack.conversationsplus.services.filetransfer.http.download; + +import android.os.PowerManager; + +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.http.ProgressListener; +import de.thedevstack.conversationsplus.services.filetransfer.FileTransferEntity; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; + +/** + * + */ +public class HttpDownloadFileTransferEntity extends FileTransferEntity implements ProgressListener { + public PowerManager.WakeLock wakeLock; + /** + * 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 HttpDownloadFileTransferEntity(Message message) { + super(message, false); + FileParams fileParams = message.getFileParams(); + this.initFile(); + this.getFile().setExpectedSize(fileParams.getSize()); + } + + /** + * Returns the global transferable status. + * + * @return {@value STATUS_FAILED} if #isFailed returns <code>true</code>, {@value STATUS_DOWNLOADING} otherwise + */ + @Override + public int getStatus() { + int status = (isFailed()) ? STATUS_FAILED : STATUS_DOWNLOADING; + return status; + } + + @Override + public void update(long bytesRead, long contentLength, boolean done) { + if (0 >= getFile().getExpectedSize()) { + getFile().setExpectedSize(contentLength); + getMessage().getFileParams().setSize(contentLength); + } + if (done) { + this.transferred(); + } else { + this.updateProgress(bytesRead); + } + UiUpdateHelper.updateConversationUi(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferService.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferService.java new file mode 100644 index 00000000..c7fcc430 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpDownloadFileTransferService.java @@ -0,0 +1,65 @@ +package de.thedevstack.conversationsplus.services.filetransfer.http.download; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.http.HttpClient; +import de.thedevstack.conversationsplus.services.filetransfer.AbstractFileTransferService; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; +import okhttp3.Call; + +/** + * + */ +public class HttpDownloadFileTransferService extends AbstractFileTransferService { + /** + * 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 + */ + @Override + 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 + */ + @Override + public boolean transferFile(Message message, boolean delay) { + Logging.d("http-download", "Get file from remote host"); + final HttpDownloadFileTransferEntity entity = new HttpDownloadFileTransferEntity(message); + if (message.getFileParams().getFileStatus() == FileStatus.NEEDS_DOWNLOAD + && ConversationsPlusApplication.hasStoragePermission() + && XmppConnectionServiceAccessor.xmppConnectionService.isDownloadAllowedInConnection()) { + FileParams fileParams = message.getFileParams(); + MessageUtil.setAndSaveFileStatus(message, FileStatus.DOWNLOADING); + entity.wakeLock = ConversationsPlusApplication.createPartialWakeLock("http_download_" + entity.getMessage().getUuid()); + entity.wakeLock.acquire(); + Call call = HttpClient.openCancelableAndProgressListenedCall(fileParams.getUrl(), entity, false); + call.enqueue(new HttpFileDownloadCallback(entity)); + } + + return true; + } + + /** + * 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 + */ + @Override + public boolean accept(Message message) { + return MessageUtil.needsDownload(message) + && message.hasFileAttached() && (null == message.getFileParams() || message.getFileParams().isRemoteAvailable()); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpFileDownloadCallback.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpFileDownloadCallback.java new file mode 100644 index 00000000..2c31754c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpFileDownloadCallback.java @@ -0,0 +1,57 @@ +package de.thedevstack.conversationsplus.services.filetransfer.http.download; + +import java.io.IOException; +import java.io.OutputStream; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.entities.DownloadableFile; +import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.persistance.FileBackend; +import de.thedevstack.conversationsplus.services.AbstractConnectionManager; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +/** + * + */ +public class HttpFileDownloadCallback implements Callback { + private HttpDownloadFileTransferEntity entity; + + public HttpFileDownloadCallback(HttpDownloadFileTransferEntity entity) { + this.entity = entity; + } + + + @Override + public void onFailure(Call call, IOException e) { + changeStatus(FileStatus.DOWNLOAD_FAILED); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + if (response.isSuccessful()) { + Logging.d("http-download", "Receiving file from remote host"); + DownloadableFile file = this.entity.getFile(); + OutputStream os = AbstractConnectionManager.createOutputStream(file, true); + os.write(response.body().bytes()); + StreamUtil.close(os); + FileBackend.updateMediaScanner(file, XmppConnectionServiceAccessor.xmppConnectionService); + this.entity.transferred(); + changeStatus(FileStatus.DOWNLOADED); + } else { + Logging.e("http-download", "Failed to retrieve file from remote host. HTTP response: " + response.code() + ", " + response.body().string()); + changeStatus(FileStatus.DOWNLOAD_FAILED); + } + if (entity.wakeLock.isHeld()) { + entity.wakeLock.release(); + } + } + + private void changeStatus(FileStatus status) { + MessageUtil.setAndSaveFileStatus(this.entity.getMessage(), status); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpRetrieveHead.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpRetrieveHead.java new file mode 100644 index 00000000..8d23d9c0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/download/HttpRetrieveHead.java @@ -0,0 +1,112 @@ +package de.thedevstack.conversationsplus.services.filetransfer.http.download; + +import java.io.IOException; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.http.Http; +import de.thedevstack.conversationsplus.http.HttpClient; +import de.thedevstack.conversationsplus.http.HttpHeadRetrievedListener; +import de.thedevstack.conversationsplus.persistance.DatabaseBackend; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.Response; + +/** + * + */ +public class HttpRetrieveHead implements Http, Callback { + private static final String LOGTAG = "http-retrieve-head"; + + private Message message; + private String url; + + private HttpHeadRetrievedListener listener; + + public HttpRetrieveHead(Message message) { + this.message = message; + this.url = (null != message && null != message.getFileParams()) ? message.getFileParams().getUrl() : null; + if (null == this.url) { + /* + * If this code is reached and the URL is null something went wrong. + * Try again to extract the file parameters from the message. + */ + MessageUtil.extractFileParamsFromBody(message); + this.url = (null != message.getFileParams()) ? message.getFileParams().getUrl() : null; + if (null == this.url) { + message.setTreatAsDownloadable(Message.Decision.NEVER); // TODO find sth better + if (null != message.getFileParams()) { + MessageUtil.setAndSaveFileStatus(message, FileStatus.NOT_FOUND); + } + } + } + } + + public void retrieveAndSetContentTypeAndLength() { + if (null != this.url) { + Logging.d(LOGTAG, "retrieve file size and mime type."); + + try { + MessageUtil.setAndSaveFileStatus(message, FileStatus.CHECKING_FILE_SIZE); + HttpClient.retrieveHead(this.url, this); + } catch (IOException e) { + Logging.e(LOGTAG, "Error while trying to call '" + url + "'.", e); + } + } + } + + private static long parseContentLength(String contentLength) { + long length = -1; + if (null != contentLength) { + try { + length = Long.parseLong(contentLength, 10); + } catch (NumberFormatException e) { + } + } + return length; + } + + @Override + public void onFailure(Call call, IOException e) { + Logging.e(LOGTAG, "Error while trying to call '" + call.request().url() + "'.", e); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + Logging.d(LOGTAG, "Response for retrieving file size and mime type received."); + FileParams fileParams = message.getFileParams(); + if (response.isSuccessful()) { + MediaType mediaType = response.body().contentType(); + String contentType = null != mediaType ? (mediaType.type() + "/" + mediaType.subtype()) : response.header(MIME_REQUEST_PROPERTY_NAME); + String contentLength = response.header(HEADER_NAME_CONTENT_LENGTH); + + long size = parseContentLength(contentLength); + fileParams.setSize(size); + fileParams.setMimeType(contentType); + if (0 < size) { + fileParams.setFileStatus(FileStatus.NEEDS_DOWNLOAD); + } + DatabaseBackend.getInstance().updateMessage(message); + UiUpdateHelper.updateConversationUi(); + if (null != this.listener) { + this.listener.onFileSizeRetrieved(size, this.message); + } + } else { + if (response.code() == HTTP_NOT_FOUND) { + Logging.d(LOGTAG, "remote file '" + response.request().url() + "' not found."); + MessageUtil.setAndSaveFileStatus(message, FileStatus.NOT_FOUND); + } else { + Logging.d(LOGTAG, "remote file '" + response.request().url() + "' not loaded - response code: " + response.code() + "."); + } + } + } + + public void setListener(HttpHeadRetrievedListener listener) { + this.listener = listener; + } +} |