Make progress more multi-repo aware.
Changed strings.xml to reflect the multi-repo nature of updating. Also refactored progress events to make them more generic and easier to nest deeply down the call stack. The ProgressListener now just expects a ProgressListener.Event, which in addition to statically typed type and progress info, also has an associated Bundle which can store arbitrary data.
This commit is contained in:
parent
4bcf4bf60d
commit
841ec9d289
res/values
src/org/fdroid/fdroid
@ -153,14 +153,15 @@
|
||||
<string name="category_recentlyupdated">Recently Updated</string>
|
||||
|
||||
<!--
|
||||
status_download takes three parameters:
|
||||
status_download takes four parameters:
|
||||
- Repository (url)
|
||||
- Downloaded size (human readable)
|
||||
- Total size (human readable)
|
||||
- Percentage complete (int between 0-100)
|
||||
-->
|
||||
<string name="status_download">Downloading %1$s / %2$s (%3$d%%)</string>
|
||||
<string name="status_processing_xml">Processing application %1$d of %2$d</string>
|
||||
<string name="status_connecting_to_repo">Connecting to repository:\n%1$s</string>
|
||||
<string name="status_checking_compatibility">Checking apps compatibility with your device…</string>
|
||||
<string name="status_download">Downloading\n%2$s / %3$s (%4$d%%) from\n%1$s</string>
|
||||
<string name="status_processing_xml">Processing application\n%2$d of %3$d from\n%1$s</string>
|
||||
<string name="status_connecting_to_repo">Connecting to\n%1$s</string>
|
||||
<string name="status_checking_compatibility">Checking all apps compatibility with your device…</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1,7 +1,54 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public interface ProgressListener {
|
||||
|
||||
public void onProgress(int type, int progress, int total);
|
||||
public void onProgress(Event event);
|
||||
|
||||
// I went a bit overboard with the overloaded constructors, but they all
|
||||
// seemed potentially useful and unambiguous, so I just put them in there
|
||||
// while I'm here.
|
||||
public static class Event {
|
||||
|
||||
public static final int NO_VALUE = Integer.MIN_VALUE;
|
||||
|
||||
public final int type;
|
||||
public final Bundle data;
|
||||
|
||||
// These two are not final, so that you can create a template Event,
|
||||
// pass it into a function which performs something over time, and
|
||||
// that function can initialize "total" and progressively
|
||||
// update "progress"
|
||||
public int progress;
|
||||
public int total;
|
||||
|
||||
public Event(int type) {
|
||||
this(type, NO_VALUE, NO_VALUE, null);
|
||||
}
|
||||
|
||||
public Event(int type, Bundle data) {
|
||||
this(type, NO_VALUE, NO_VALUE, data);
|
||||
}
|
||||
|
||||
public Event(int type, int progress) {
|
||||
this(type, progress, NO_VALUE, null);
|
||||
}
|
||||
|
||||
public Event(int type, int progress, Bundle data) {
|
||||
this(type, NO_VALUE, NO_VALUE, data);
|
||||
}
|
||||
|
||||
public Event(int type, int progress, int total) {
|
||||
this(type, progress, total, null);
|
||||
}
|
||||
|
||||
public Event(int type, int progress, int total, Bundle data) {
|
||||
this.type = type;
|
||||
this.progress = progress;
|
||||
this.total = total;
|
||||
this.data = data == null ? new Bundle() : data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import android.os.Bundle;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
@ -55,8 +56,8 @@ import android.util.Log;
|
||||
|
||||
public class RepoXMLHandler extends DefaultHandler {
|
||||
|
||||
// The ID of the repo we're processing.
|
||||
private int repo;
|
||||
// The repo we're processing.
|
||||
private DB.Repo repo;
|
||||
|
||||
private Vector<DB.App> apps;
|
||||
|
||||
@ -73,11 +74,13 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
public static final int PROGRESS_TYPE_DOWNLOAD = 1;
|
||||
public static final int PROGRESS_TYPE_PROCESS_XML = 2;
|
||||
|
||||
public static final String PROGRESS_DATA_REPO = "repo";
|
||||
|
||||
// The date format used in the repo XML file.
|
||||
private SimpleDateFormat mXMLDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private int totalAppCount;
|
||||
|
||||
public RepoXMLHandler(int repo, Vector<DB.App> apps, ProgressListener listener) {
|
||||
public RepoXMLHandler(DB.Repo repo, Vector<DB.App> apps, ProgressListener listener) {
|
||||
this.repo = repo;
|
||||
this.apps = apps;
|
||||
pubkey = null;
|
||||
@ -228,28 +231,37 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
curapp.requirements = DB.CommaSeparatedList.make(str);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Bundle createProgressData(String repoAddress) {
|
||||
Bundle data = new Bundle();
|
||||
data.putString(PROGRESS_DATA_REPO, repoAddress);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName,
|
||||
Attributes attributes) throws SAXException {
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
if (localName == "repo") {
|
||||
if (localName.equals("repo")) {
|
||||
String pk = attributes.getValue("", "pubkey");
|
||||
if (pk != null)
|
||||
pubkey = pk;
|
||||
} else if (localName == "application" && curapp == null) {
|
||||
} else if (localName.equals("application") && curapp == null) {
|
||||
curapp = new DB.App();
|
||||
curapp.detail_Populated = true;
|
||||
Bundle progressData = createProgressData(repo.address);
|
||||
progressCounter ++;
|
||||
progressListener.onProgress(RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML, progressCounter, totalAppCount);
|
||||
} else if (localName == "package" && curapp != null && curapk == null) {
|
||||
progressListener.onProgress(
|
||||
new ProgressListener.Event(
|
||||
RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML, progressCounter,
|
||||
totalAppCount, progressData));
|
||||
} else if (localName.equals("package") && curapp != null && curapk == null) {
|
||||
curapk = new DB.Apk();
|
||||
curapk.id = curapp.id;
|
||||
curapk.repo = repo;
|
||||
curapk.repo = repo.id;
|
||||
hashType = null;
|
||||
} else if (localName == "hash" && curapk != null) {
|
||||
} else if (localName.equals("hash") && curapk != null) {
|
||||
hashType = attributes.getValue("", "type");
|
||||
}
|
||||
curchars.setLength(0);
|
||||
@ -263,7 +275,8 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
// empty if none was available.
|
||||
private static int getRemoteFile(Context ctx, String url, String dest,
|
||||
String etag, StringBuilder retag,
|
||||
ProgressListener progressListener ) throws MalformedURLException,
|
||||
ProgressListener progressListener,
|
||||
ProgressListener.Event progressEvent) throws MalformedURLException,
|
||||
IOException {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@ -280,14 +293,14 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
// - 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.
|
||||
int size = connection.getContentLength();
|
||||
Log.d("FDroid", "Downloading " + size + " bytes from " + url);
|
||||
progressEvent.total = connection.getContentLength();
|
||||
Log.d("FDroid", "Downloading " + progressEvent.total + " bytes from " + url);
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
try {
|
||||
input = connection.getInputStream();
|
||||
output = ctx.openFileOutput(dest, Context.MODE_PRIVATE);
|
||||
Utils.copy(input, output, size, progressListener, PROGRESS_TYPE_DOWNLOAD);
|
||||
Utils.copy(input, output, progressListener, progressEvent);
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
@ -329,8 +342,11 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
address += "?" + pi.versionName;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
Bundle progressData = createProgressData(repo.address);
|
||||
ProgressListener.Event event = new ProgressListener.Event(
|
||||
RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, progressData);
|
||||
code = getRemoteFile(ctx, address, "tempindex.jar",
|
||||
repo.lastetag, newetag, progressListener);
|
||||
repo.lastetag, newetag, progressListener, event );
|
||||
if (code == 200) {
|
||||
String jarpath = ctx.getFilesDir() + "/tempindex.jar";
|
||||
JarFile jar = null;
|
||||
@ -385,8 +401,12 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
|
||||
// It's an old-fashioned unsigned repo...
|
||||
Log.d("FDroid", "Getting unsigned index from " + repo.address);
|
||||
Bundle eventData = createProgressData(repo.address);
|
||||
ProgressListener.Event event = new ProgressListener.Event(
|
||||
RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, eventData);
|
||||
code = getRemoteFile(ctx, repo.address + "/index.xml",
|
||||
"tempindex.xml", repo.lastetag, newetag, progressListener);
|
||||
"tempindex.xml", repo.lastetag, newetag,
|
||||
progressListener, event);
|
||||
}
|
||||
|
||||
if (code == 200) {
|
||||
@ -394,7 +414,7 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
XMLReader xr = sp.getXMLReader();
|
||||
RepoXMLHandler handler = new RepoXMLHandler(repo.id, apps, progressListener);
|
||||
RepoXMLHandler handler = new RepoXMLHandler(repo, apps, progressListener);
|
||||
xr.setContentHandler(handler);
|
||||
|
||||
File tempIndex = new File(ctx.getFilesDir() + "/tempindex.xml");
|
||||
|
@ -330,16 +330,18 @@ public class UpdateService extends IntentService implements ProgressListener {
|
||||
* It could be progress downloading from the repo, or perhaps processing the info from the repo.
|
||||
*/
|
||||
@Override
|
||||
public void onProgress(int type, int progress, int total) {
|
||||
public void onProgress(ProgressListener.Event event) {
|
||||
|
||||
String message = "";
|
||||
if (type == RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD) {
|
||||
String downloadedSize = Utils.getFriendlySize( progress );
|
||||
String totalSize = Utils.getFriendlySize( total );
|
||||
int percent = (int)((double)progress/total * 100);
|
||||
message = getString(R.string.status_download, downloadedSize, totalSize, percent);
|
||||
} else if (type == RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML) {
|
||||
message = getString(R.string.status_processing_xml, progress, total);
|
||||
if (event.type == RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD) {
|
||||
String repoAddress = event.data.getString(RepoXMLHandler.PROGRESS_DATA_REPO);
|
||||
String downloadedSize = Utils.getFriendlySize( event.progress );
|
||||
String totalSize = Utils.getFriendlySize( event.total );
|
||||
int percent = (int)((double)event.progress/event.total * 100);
|
||||
message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent);
|
||||
} else if (event.type == RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML) {
|
||||
String repoAddress = event.data.getString(RepoXMLHandler.PROGRESS_DATA_REPO);
|
||||
message = getString(R.string.status_processing_xml, repoAddress, event.progress, event.total);
|
||||
}
|
||||
|
||||
sendStatus(STATUS_INFO, message);
|
||||
|
@ -18,18 +18,9 @@
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
|
||||
public final class Utils {
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
public static final int BUFFER_SIZE = 4096;
|
||||
|
||||
@ -37,28 +28,31 @@ public final class Utils {
|
||||
"%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB" };
|
||||
|
||||
|
||||
public static void copy(InputStream input, OutputStream output)
|
||||
throws IOException {
|
||||
copy(input, output, -1, null, -1);
|
||||
}
|
||||
public static void copy(InputStream input, OutputStream output)
|
||||
throws IOException {
|
||||
copy(input, output, null, null);
|
||||
}
|
||||
|
||||
public static void copy(InputStream input, OutputStream output, int totalSize, ProgressListener progressListener, int progressType)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead = 0;
|
||||
while (true) {
|
||||
int count = input.read(buffer);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
if (progressListener != null) {
|
||||
bytesRead += count;
|
||||
progressListener.onProgress(progressType, bytesRead, totalSize);
|
||||
}
|
||||
output.write(buffer, 0, count);
|
||||
}
|
||||
output.flush();
|
||||
}
|
||||
public static void copy(InputStream input, OutputStream output,
|
||||
ProgressListener progressListener,
|
||||
ProgressListener.Event templateProgressEvent)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead = 0;
|
||||
while (true) {
|
||||
int count = input.read(buffer);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
if (progressListener != null) {
|
||||
bytesRead += count;
|
||||
templateProgressEvent.progress = bytesRead;
|
||||
progressListener.onProgress(templateProgressEvent);
|
||||
}
|
||||
output.write(buffer, 0, count);
|
||||
}
|
||||
output.flush();
|
||||
}
|
||||
|
||||
public static void closeQuietly(Closeable closeable) {
|
||||
if (closeable == null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user