diff --git a/src/org/fdroid/fdroid/net/Downloader.java b/src/org/fdroid/fdroid/net/Downloader.java index 27fc6bb79..922256c3f 100644 --- a/src/org/fdroid/fdroid/net/Downloader.java +++ b/src/org/fdroid/fdroid/net/Downloader.java @@ -5,24 +5,18 @@ import java.net.*; import android.content.*; import org.fdroid.fdroid.*; -public class Downloader { +public abstract class Downloader { - private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; - private static final String HEADER_FIELD_ETAG = "ETag"; - - private URL sourceUrl; private OutputStream outputStream; private ProgressListener progressListener = null; private ProgressListener.Event progressEvent = null; - private String eTag = null; private final File outputFile; - private HttpURLConnection connection; - private int statusCode = -1; + + public abstract InputStream inputStream() throws IOException; // The context is required for opening the file to write to. - public Downloader(String source, String destFile, Context ctx) + public Downloader(String destFile, Context ctx) throws FileNotFoundException, MalformedURLException { - sourceUrl = new URL(source); outputStream = ctx.openFileOutput(destFile, Context.MODE_PRIVATE); outputFile = new File(ctx.getFilesDir() + File.separator + destFile); } @@ -32,16 +26,14 @@ public class Downloader { * you are done*. * @see org.fdroid.fdroid.net.Downloader#getFile() */ - public Downloader(String source, Context ctx) throws IOException { + public Downloader(Context ctx) throws IOException { // http://developer.android.com/guide/topics/data/data-storage.html#InternalCache outputFile = File.createTempFile("dl-", "", ctx.getCacheDir()); outputStream = new FileOutputStream(outputFile); - sourceUrl = new URL(source); } - public Downloader(String source, OutputStream output) + public Downloader(OutputStream output) throws MalformedURLException { - sourceUrl = new URL(source); outputStream = output; outputFile = null; } @@ -61,82 +53,25 @@ public class Downloader { return outputFile; } - /** - * Only available after downloading a file. - */ - public int getStatusCode() { - return statusCode; - } - - /** - * If you ask for the eTag before calling download(), you will get the - * same one you passed in (if any). If you call it after download(), you - * will get the new eTag from the server, or null if there was none. - */ - public String getETag() { - return eTag; - } - - /** - * If this eTag matches that returned by the server, then no download will - * take place, and a status code of 304 will be returned by download(). - */ - public void setETag(String eTag) { - this.eTag = eTag; - } - - // Get a remote file. Returns the HTTP response code. - // If 'etag' is not null, it's passed to the server as an If-None-Match - // header, in which case expect a 304 response if nothing changed. - // In the event of a 200 response ONLY, 'retag' (which should be passed - // empty) may contain an etag value for the response, or it may be left - // empty if none was available. - public int download() throws IOException { - connection = (HttpURLConnection)sourceUrl.openConnection(); - setupCacheCheck(); - statusCode = connection.getResponseCode(); - if (statusCode == 200) { - setupProgressListener(); - InputStream input = null; - try { - input = connection.getInputStream(); - Utils.copy(input, outputStream, - progressListener, progressEvent); - } finally { - Utils.closeQuietly(outputStream); - Utils.closeQuietly(input); - } - updateCacheCheck(); - } - return statusCode; - } - - protected void setupCacheCheck() { - if (eTag != null) { - connection.setRequestProperty(HEADER_IF_NONE_MATCH, eTag); - } - } - - protected void updateCacheCheck() { - eTag = connection.getHeaderField(HEADER_FIELD_ETAG); - } - - protected void setupProgressListener() { + private void setupProgressListener() { if (progressListener != null && progressEvent != null) { - // Testing in the emulator for me, showed that figuring out the - // filesize took about 1 to 1.5 seconds. - // To put this in context, downloading a repo of: - // - 400k takes ~6 seconds - // - 5k takes ~3 seconds - // on my connection. I think the 1/1.5 seconds is worth it, - // because as the repo grows, the tradeoff will - // become more worth it. - progressEvent.total = connection.getContentLength(); + progressEvent.total = totalDownloadSize(); } } - public boolean hasChanged() { - return this.statusCode == 200; + protected abstract int totalDownloadSize(); + + public void download() throws IOException { + setupProgressListener(); + InputStream input = null; + try { + input = inputStream(); + Utils.copy(input, outputStream, + progressListener, progressEvent); + } finally { + Utils.closeQuietly(outputStream); + Utils.closeQuietly(input); + } } } diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java new file mode 100644 index 000000000..363ff5f49 --- /dev/null +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -0,0 +1,115 @@ +package org.fdroid.fdroid.net; + +import android.content.Context; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public class HttpDownloader extends Downloader { + + private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; + private static final String HEADER_FIELD_ETAG = "ETag"; + + private URL sourceUrl; + private String eTag = null; + private HttpURLConnection connection; + private int statusCode = -1; + + // The context is required for opening the file to write to. + public HttpDownloader(String source, String destFile, Context ctx) + throws FileNotFoundException, MalformedURLException { + super(destFile, ctx); + sourceUrl = new URL(source); + } + + /** + * Downloads to a temporary file, which *you must delete yourself when + * you are done*. + * @see org.fdroid.fdroid.net.HttpDownloader#getFile() + */ + public HttpDownloader(String source, Context ctx) throws IOException { + super(ctx); + sourceUrl = new URL(source); + } + + public HttpDownloader(String source, OutputStream output) + throws MalformedURLException { + super(output); + sourceUrl = new URL(source); + } + + public InputStream inputStream() throws IOException { + return connection.getInputStream(); + } + + // Get a remote file. Returns the HTTP response code. + // If 'etag' is not null, it's passed to the server as an If-None-Match + // header, in which case expect a 304 response if nothing changed. + // In the event of a 200 response ONLY, 'retag' (which should be passed + // empty) may contain an etag value for the response, or it may be left + // empty if none was available. + public int downloadHttpFile() throws IOException { + connection = (HttpURLConnection)sourceUrl.openConnection(); + setupCacheCheck(); + statusCode = connection.getResponseCode(); + if (statusCode == 200) { + download(); + updateCacheCheck(); + } + return statusCode; + } + + /** + * Only available after downloading a file. + */ + public int getStatusCode() { + return statusCode; + } + + /** + * If you ask for the eTag before calling download(), you will get the + * same one you passed in (if any). If you call it after download(), you + * will get the new eTag from the server, or null if there was none. + */ + public String getETag() { + return eTag; + } + + /** + * If this eTag matches that returned by the server, then no download will + * take place, and a status code of 304 will be returned by download(). + */ + public void setETag(String eTag) { + this.eTag = eTag; + } + + + protected void setupCacheCheck() { + if (eTag != null) { + connection.setRequestProperty(HEADER_IF_NONE_MATCH, eTag); + } + } + + protected void updateCacheCheck() { + eTag = connection.getHeaderField(HEADER_FIELD_ETAG); + } + + // Testing in the emulator for me, showed that figuring out the + // filesize took about 1 to 1.5 seconds. + // To put this in context, downloading a repo of: + // - 400k takes ~6 seconds + // - 5k takes ~3 seconds + // on my connection. I think the 1/1.5 seconds is worth it, + // because as the repo grows, the tradeoff will + // become more worth it. + protected int totalDownloadSize() { + return connection.getContentLength(); + } + + public boolean hasChanged() { + return this.statusCode == 200; + } + +} diff --git a/src/org/fdroid/fdroid/updater/RepoUpdater.java b/src/org/fdroid/fdroid/updater/RepoUpdater.java index 486798154..a5817cc50 100644 --- a/src/org/fdroid/fdroid/updater/RepoUpdater.java +++ b/src/org/fdroid/fdroid/updater/RepoUpdater.java @@ -12,6 +12,7 @@ import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.net.Downloader; +import org.fdroid.fdroid.net.HttpDownloader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -84,11 +85,11 @@ abstract public class RepoUpdater { protected abstract String getIndexAddress(); - protected Downloader downloadIndex() throws UpdateException { + protected HttpDownloader downloadIndex() throws UpdateException { Bundle progressData = createProgressData(repo.address); - Downloader downloader = null; + HttpDownloader downloader = null; try { - downloader = new Downloader(getIndexAddress(), context); + downloader = new HttpDownloader(getIndexAddress(), context); downloader.setETag(repo.lastetag); if (isInteractive()) { @@ -98,7 +99,7 @@ abstract public class RepoUpdater { downloader.setProgressListener(progressListener, event); } - int status = downloader.download(); + int status = downloader.downloadHttpFile(); if (status == 304) { // The index is unchanged since we last read it. We just mark @@ -169,7 +170,7 @@ abstract public class RepoUpdater { File indexFile = null; try { - Downloader downloader = downloadIndex(); + HttpDownloader downloader = downloadIndex(); hasChanged = downloader.hasChanged(); if (hasChanged) {