From 754de6bb0449a577d2bb9c28cca6adf0ef9554f6 Mon Sep 17 00:00:00 2001 From: steckbrief Date: Mon, 6 Feb 2017 10:01:13 +0100 Subject: relates FS#241: Implementation of http download based on okhttp --- .../thedevstack/conversationsplus/http/Http.java | 18 + .../conversationsplus/http/HttpClient.java | 152 ++++++++- .../http/HttpConnectionManager.java | 90 ----- .../http/HttpDownloadConnection.java | 375 --------------------- .../http/HttpHeadRetrievedListener.java | 10 + .../conversationsplus/http/ProgressListener.java | 14 + 6 files changed, 178 insertions(+), 481 deletions(-) create mode 100644 src/main/java/de/thedevstack/conversationsplus/http/Http.java delete mode 100644 src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java delete mode 100644 src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java create mode 100644 src/main/java/de/thedevstack/conversationsplus/http/HttpHeadRetrievedListener.java create mode 100644 src/main/java/de/thedevstack/conversationsplus/http/ProgressListener.java (limited to 'src/main/java/de/thedevstack/conversationsplus/http') diff --git a/src/main/java/de/thedevstack/conversationsplus/http/Http.java b/src/main/java/de/thedevstack/conversationsplus/http/Http.java new file mode 100644 index 00000000..98b4dc82 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/http/Http.java @@ -0,0 +1,18 @@ +package de.thedevstack.conversationsplus.http; + +import java.net.HttpURLConnection; + +/** + * + */ + +public interface Http { + // HTTP Response Codes + int HTTP_NOT_FOUND = HttpURLConnection.HTTP_NOT_FOUND; + + // Header Field Names + String MIME_REQUEST_PROPERTY_NAME = "Content-Type"; + String HEADER_NAME_CONTENT_LENGTH = "Content-Length"; + String HEADER_NAME_ACCEPT_ENCODING = "Accept-Encoding"; + String USER_AGENT_REQUEST_PROPERTY_NAME = "User-Agent"; +} diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpClient.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpClient.java index 7e12a890..e62be056 100644 --- a/src/main/java/de/thedevstack/conversationsplus/http/HttpClient.java +++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpClient.java @@ -1,52 +1,101 @@ package de.thedevstack.conversationsplus.http; +import android.support.annotation.NonNull; + import org.apache.http.conn.ssl.StrictHostnameVerifier; +import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.Locale; import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.utils.CryptoHelper; import de.thedevstack.conversationsplus.utils.SSLSocketHelper; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Interceptor; +import okhttp3.MediaType; import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSource; +import okio.ForwardingSource; +import okio.Okio; +import okio.Source; /** - * Created by steckbrief on 22.08.2016. + * */ -public final class HttpClient { +public final class HttpClient implements Http { private static HttpClient INSTANCE; - private boolean interactive = false; - private OkHttpClient client; + private static final String LOGTAG = "http-client"; + + private final OkHttpClient client; - public static void init() { + public static synchronized void init() { INSTANCE = new HttpClient(); } - public static synchronized OkHttpClient getClient(boolean interactive) { - if (INSTANCE.interactive != interactive) { - INSTANCE.interactive = interactive; - INSTANCE.buildClient(); + public static synchronized HttpClient getClient() { + if (null == INSTANCE) { + init(); } - return INSTANCE.client; + return INSTANCE; } - private HttpClient() { - this.buildClient(); + private static OkHttpClient.Builder getBuilder(boolean interactive) { + OkHttpClient.Builder builder = INSTANCE.client.newBuilder(); + INSTANCE.initTrustManager(builder, interactive); + + return builder; + } + + public static synchronized OkHttpClient getOkHttpClient(boolean interactive) { + return getBuilder(interactive).build(); + } + + public static synchronized Call openCancelableAndProgressListenedCall(String url, final ProgressListener progressListener, boolean interactive) { + OkHttpClient.Builder builder = getBuilder(interactive); + OkHttpClient client = builder.addNetworkInterceptor(new Interceptor() { + @Override public Response intercept(Chain chain) throws IOException { + Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }) + .build(); + + return client.newCall(new Request.Builder().url(url).build()); } - private void buildClient() { + public static void retrieveHead(String url, @NonNull Callback callback) throws IOException { + OkHttpClient client = HttpClient.getOkHttpClient(true); + Request request = new Request.Builder() + .url(url) + //.addHeader(HEADER_NAME_ACCEPT_ENCODING, "") + .head() + .build(); + client.newCall(request).enqueue(callback); + } + + private HttpClient() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); - this.initTrustManager(builder); + builder.addInterceptor(new UserAgentInterceptor()); + builder.addInterceptor(new LoggingInterceptor()); this.client = builder.build(); } - public void initTrustManager(final OkHttpClient.Builder builder) { + private static void initTrustManager(final OkHttpClient.Builder builder, final boolean interactive) { final X509TrustManager trustManager; final HostnameVerifier hostnameVerifier; if (interactive) { @@ -78,4 +127,75 @@ public final class HttpClient { } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { } } + + private static class UserAgentInterceptor implements Interceptor, Http { + + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + Request requestWithUserAgent = originalRequest.newBuilder() + .header(USER_AGENT_REQUEST_PROPERTY_NAME, ConversationsPlusApplication.getNameAndVersion()) + .build(); + return chain.proceed(requestWithUserAgent); + } + } + + private static class LoggingInterceptor implements Interceptor { + @Override public Response intercept(Interceptor.Chain chain) throws IOException { + Request request = chain.request(); + + long t1 = System.nanoTime(); + Logging.d(LOGTAG, String.format(Locale.getDefault(), "Sending %s request %s on %s%n%s", + request.method(), request.url(), chain.connection(), request.headers())); + + Response response = chain.proceed(request); + + long t2 = System.nanoTime(); + Logging.d(LOGTAG, String.format(Locale.getDefault(), "Received response for %s request %s in %.1fms%n%s", + response.request().method(), response.request().url(), (t2 - t1) / 1e6d, response.headers())); + + return response; + } + } + + private static class ProgressResponseBody extends ResponseBody { + + private final ResponseBody responseBody; + private final ProgressListener progressListener; + private BufferedSource bufferedSource; + + public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) { + this.responseBody = responseBody; + this.progressListener = progressListener; + } + + @Override public MediaType contentType() { + return responseBody.contentType(); + } + + @Override public long contentLength() { + return responseBody.contentLength(); + } + + @Override public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + + @Override public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + boolean done = bytesRead == -1; + long currentBytesRead = !done ? bytesRead : 0; + progressListener.update(currentBytesRead, responseBody.contentLength(), done); + return bytesRead; + } + }; + } + } } diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java deleted file mode 100644 index 011e2529..00000000 --- a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java +++ /dev/null @@ -1,90 +0,0 @@ -package de.thedevstack.conversationsplus.http; - -import org.apache.http.conn.ssl.StrictHostnameVerifier; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.X509TrustManager; - -import de.thedevstack.conversationsplus.ConversationsPlusApplication; -import de.thedevstack.conversationsplus.entities.Message; -import de.thedevstack.conversationsplus.services.AbstractConnectionManager; -import de.thedevstack.conversationsplus.utils.CryptoHelper; -import de.thedevstack.conversationsplus.utils.MessageUtil; -import de.thedevstack.conversationsplus.utils.SSLSocketHelper; - -public class HttpConnectionManager extends AbstractConnectionManager { - private static HttpConnectionManager INSTANCE; - - public static void init() { - INSTANCE = new HttpConnectionManager(); - } - - private List downloadConnections = new CopyOnWriteArrayList<>(); - - public static HttpDownloadConnection createNewDownloadConnection(Message message) { - return createNewDownloadConnection(message, false); - } - - public static HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) { - if (MessageUtil.needsDownload(message)) { - HttpDownloadConnection connection = new HttpDownloadConnection(INSTANCE); - connection.init(message, interactive); - INSTANCE.downloadConnections.add(connection); - return connection; - } - return null; - } - - public void finishConnection(HttpDownloadConnection connection) { - this.downloadConnections.remove(connection); - } - - public static void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { - final X509TrustManager trustManager; - final HostnameVerifier hostnameVerifier; - if (interactive) { - trustManager = ConversationsPlusApplication.getMemorizingTrustManager(); - hostnameVerifier = ConversationsPlusApplication.getMemorizingTrustManager().wrapHostnameVerifier( - new StrictHostnameVerifier()); - } else { - trustManager = ConversationsPlusApplication.getMemorizingTrustManager() - .getNonInteractive(); - hostnameVerifier = ConversationsPlusApplication.getMemorizingTrustManager() - .wrapHostnameVerifierNonInteractive( - new StrictHostnameVerifier()); - } - try { - final SSLContext sc = SSLSocketHelper.getSSLContext(); - sc.init(null, new X509TrustManager[]{trustManager}, - ConversationsPlusApplication.getSecureRandom()); - - final SSLSocketFactory sf = sc.getSocketFactory(); - final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( - sf.getSupportedCipherSuites()); - if (cipherSuites.length > 0) { - sc.getDefaultSSLParameters().setCipherSuites(cipherSuites); - - } - - connection.setSSLSocketFactory(sf); - connection.setHostnameVerifier(hostnameVerifier); - } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { - } - } - - public Proxy getProxy() throws IOException { - return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getLocalHost(), 8118)); - } -} diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java deleted file mode 100644 index 07f308fe..00000000 --- a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java +++ /dev/null @@ -1,375 +0,0 @@ -package de.thedevstack.conversationsplus.http; - -import android.os.PowerManager; -import android.util.Log; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.CancellationException; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLHandshakeException; - -import de.thedevstack.android.logcat.Logging; -import de.thedevstack.conversationsplus.ConversationsPlusApplication; -import de.thedevstack.conversationsplus.ConversationsPlusPreferences; -import de.thedevstack.conversationsplus.entities.FileParams; -import de.thedevstack.conversationsplus.enums.FileStatus; -import de.thedevstack.conversationsplus.exceptions.RemoteFileNotFoundException; -import de.thedevstack.conversationsplus.utils.MessageUtil; -import de.thedevstack.conversationsplus.utils.StreamUtil; -import de.thedevstack.conversationsplus.Config; -import de.thedevstack.conversationsplus.R; -import de.thedevstack.conversationsplus.entities.DownloadableFile; -import de.thedevstack.conversationsplus.entities.Message; -import de.thedevstack.conversationsplus.entities.Transferable; -import de.thedevstack.conversationsplus.entities.TransferablePlaceholder; -import de.thedevstack.conversationsplus.persistance.FileBackend; -import de.thedevstack.conversationsplus.services.AbstractConnectionManager; -import de.thedevstack.conversationsplus.services.XmppConnectionService; -import de.thedevstack.conversationsplus.utils.CryptoHelper; -import de.thedevstack.conversationsplus.utils.FileUtils; -import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; - -public class HttpDownloadConnection implements Transferable { - - private HttpConnectionManager mHttpConnectionManager; - private XmppConnectionService mXmppConnectionService; - - private URL mUrl; - private Message message; - private DownloadableFile file; - private int mStatus = Transferable.STATUS_UNKNOWN; - private boolean acceptedAutomatically = false; - private int mProgress = 0; - private boolean canceled = false; - - public HttpDownloadConnection(HttpConnectionManager manager) { - this.mHttpConnectionManager = manager; - this.mXmppConnectionService = XmppConnectionServiceAccessor.xmppConnectionService; - } - - @Override - public boolean start() { - if (mXmppConnectionService.hasInternetConnection()) { - if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) { - checkFileSize(true); - } else { - new Thread(new FileDownloader(true)).start(); - } - return true; - } else { - return false; - } - } - - public void init(Message message) { - init(message, false); - } - - public void init(Message message, boolean interactive) { - this.message = message; - this.message.setTransferable(this); - try { - String url = (null != message && null != message.getFileParams()) ? message.getFileParams().getUrl() : null; - if (null == 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); - url = (null != message.getFileParams()) ? message.getFileParams().getUrl() : null; - if (null == url) { - message.setTreatAsDownloadable(Message.Decision.NEVER); // TODO find sth better - this.cancel(); - return; - } - } - mUrl = new URL(url); - final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/') + 1).toLowerCase(); - final String lastPart = FileUtils.getLastExtension(sUrlFilename); - - if (!lastPart.isEmpty() && ("pgp".equals(lastPart) || "gpg".equals(lastPart))) { - this.message.setEncryption(Message.ENCRYPTION_PGP); - } else if (message.getEncryption() != Message.ENCRYPTION_OTR - && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { - this.message.setEncryption(Message.ENCRYPTION_NONE); - } - - String extension; - String originalFilename; - if (!lastPart.isEmpty() && VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { - extension = FileUtils.getSecondToLastExtension(sUrlFilename); - originalFilename = sUrlFilename.replace("." + lastPart, ""); - } else { - extension = lastPart; - originalFilename = sUrlFilename; - } - message.setRelativeFilePath(message.getUuid() + "." + extension); - this.file = FileBackend.getFile(message, false); - - FileParams fileParams = message.getFileParams(); - if (null == fileParams) { - fileParams = new FileParams(); - message.setFileParams(fileParams); - } - fileParams.setOriginalFilename(originalFilename); - - if ((this.message.getEncryption() == Message.ENCRYPTION_OTR - || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL) - && this.file.getKey() == null) { - this.message.setEncryption(Message.ENCRYPTION_NONE); - } - checkFileSize(interactive); - } catch (MalformedURLException e) { - this.cancel(); - } - } - - private void checkFileSize(boolean interactive) { - new Thread(new FileSizeChecker(interactive)).start(); - } - - @Override - public void cancel() { - this.canceled = true; - mHttpConnectionManager.finishConnection(this); - if (message.isFileOrImage()) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - } else { - message.setTransferable(null); - } - mXmppConnectionService.updateConversationUi(); - } - - private void finish() { - FileBackend.updateMediaScanner(file, mXmppConnectionService); - message.setTransferable(null); - MessageUtil.setAndSaveFileStatus(this.message, FileStatus.DOWNLOADED); - mHttpConnectionManager.finishConnection(this); - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - message.getConversation().getAccount().getPgpDecryptionService().add(message); - } - mXmppConnectionService.updateConversationUi(); - if (acceptedAutomatically) { - mXmppConnectionService.getNotificationService().push(message); - } - } - - private void changeStatus(int status) { - this.mStatus = status; - mXmppConnectionService.updateConversationUi(); - } - - private void showToastForException(Exception e) { - e.printStackTrace(); - if (e instanceof java.net.UnknownHostException) { - mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found); - } else if (e instanceof java.net.ConnectException) { - mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect); - } else if (!(e instanceof CancellationException)) { - mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found); - } - } - - private class FileSizeChecker implements Runnable { - - private boolean interactive = false; - - public FileSizeChecker(boolean interactive) { - this.interactive = interactive; - } - - @Override - public void run() { - long size; - try { - size = retrieveFileSize(); - } catch (SSLHandshakeException e) { - changeStatus(STATUS_OFFER_CHECK_FILESIZE); - HttpDownloadConnection.this.acceptedAutomatically = false; - HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); - return; - } catch (RemoteFileNotFoundException e) { - message.setNoDownloadable(); // TODO Set remote file status to not-available - cancel(); - return; - } catch (IOException e) { - Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); - if (interactive) { - showToastForException(e); - } - cancel(); - return; - } - file.setExpectedSize(size); - if (mHttpConnectionManager.hasStoragePermission() - && size != -1 - && size <= ConversationsPlusPreferences.autoAcceptFileSize() - && mXmppConnectionService.isDownloadAllowedInConnection()) { - HttpDownloadConnection.this.acceptedAutomatically = true; - new Thread(new FileDownloader(interactive)).start(); - } else { - changeStatus(STATUS_OFFER); - HttpDownloadConnection.this.acceptedAutomatically = false; - HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); - } - } - - private long retrieveFileSize() throws IOException { - try { - Logging.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); - changeStatus(STATUS_CHECKING); - HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("HEAD"); - Logging.d(Config.LOGTAG, "url: " + connection.getURL().toString()); - Logging.d(Config.LOGTAG, "connection: " + connection.toString()); - connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); - // https://code.google.com/p/android/issues/detail?id=24672 - connection.setRequestProperty("Accept-Encoding", ""); - if (connection instanceof HttpsURLConnection) { - mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); - } - connection.connect(); - if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { - Logging.d(Config.LOGTAG, "remote file not found"); - throw new RemoteFileNotFoundException(); - } - String contentLength = connection.getHeaderField("Content-Length"); - connection.disconnect(); - if (contentLength == null) { - return -1; - } - return Long.parseLong(contentLength, 10); - } catch (RemoteFileNotFoundException e) { - throw e; - } catch (IOException e) { - return -1; - } catch (NumberFormatException e) { - return -1; - } - } - - } - - private class FileDownloader implements Runnable { - - private boolean interactive = false; - - private OutputStream os; - - public FileDownloader(boolean interactive) { - this.interactive = interactive; - } - - @Override - public void run() { - try { - changeStatus(STATUS_DOWNLOADING); - download(); - updateImageBounds(); - finish(); - } catch (SSLHandshakeException e) { - changeStatus(STATUS_OFFER); - } catch (Exception e) { - if (interactive) { - showToastForException(e); - } - cancel(); - } - } - - private void download() throws SSLHandshakeException, IOException { - InputStream is = null; - PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("http_download_"+message.getUuid()); - try { - wakeLock.acquire(); - HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); - if (connection instanceof HttpsURLConnection) { - mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); - } - connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); - final boolean tryResume = file.exists() && file.getKey() == null; - if (tryResume) { - Logging.d(Config.LOGTAG, "http download trying resume"); - long size = file.getSize(); - connection.setRequestProperty("Range", "bytes="+size+"-"); - } - connection.connect(); - is = new BufferedInputStream(connection.getInputStream()); - boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges")); - long transmitted = 0; - long expected = file.getExpectedSize(); - if (tryResume && serverResumed) { - Logging.d(Config.LOGTAG, "server resumed"); - transmitted = file.getSize(); - updateProgress((int) ((((double) transmitted) / expected) * 100)); - os = AbstractConnectionManager.createAppendedOutputStream(file); - } else { - file.getParentFile().mkdirs(); - file.createNewFile(); - os = AbstractConnectionManager.createOutputStream(file, true); - } - int count = -1; - byte[] buffer = new byte[1024]; - while ((count = is.read(buffer)) != -1) { - transmitted += count; - os.write(buffer, 0, count); - updateProgress((int) ((((double) transmitted) / expected) * 100)); - if (canceled) { - throw new CancellationException(); - } - } - } catch (CancellationException | IOException e) { - throw e; - } finally { - if (os != null) { - try { - os.flush(); - } catch (final IOException ignored) { - - } - } - StreamUtil.close(os); - StreamUtil.close(is); - wakeLock.release(); - } - } - - private void updateImageBounds() { - message.setType(Message.TYPE_FILE); - MessageUtil.updateFileParams(message, mUrl); - mXmppConnectionService.updateMessage(message); - } - - } - - public void updateProgress(int i) { - this.mProgress = i; - mXmppConnectionService.updateConversationUi(); - } - - @Override - public int getStatus() { - return this.mStatus; - } - - @Override - public long getFileSize() { - if (this.file != null) { - return this.file.getExpectedSize(); - } else { - return 0; - } - } - - @Override - public int getProgress() { - return this.mProgress; - } -} diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpHeadRetrievedListener.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpHeadRetrievedListener.java new file mode 100644 index 00000000..0db4f71b --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpHeadRetrievedListener.java @@ -0,0 +1,10 @@ +package de.thedevstack.conversationsplus.http; + +import de.thedevstack.conversationsplus.entities.Message; + +/** + * + */ +public interface HttpHeadRetrievedListener { + void onFileSizeRetrieved(long size, Message message); +} diff --git a/src/main/java/de/thedevstack/conversationsplus/http/ProgressListener.java b/src/main/java/de/thedevstack/conversationsplus/http/ProgressListener.java new file mode 100644 index 00000000..b834eae1 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/http/ProgressListener.java @@ -0,0 +1,14 @@ +package de.thedevstack.conversationsplus.http; + +/** + * + */ +public interface ProgressListener { + /** + * React on transferred bytes. + * @param bytesRead the number of bytes transferred in the current chunk + * @param contentLength the total number of bytes to be transferred + * @param done whether there are bytes left or not + */ + void update(long bytesRead, long contentLength, boolean done); +} -- cgit v1.2.3