2016-04-21 23:28:01 +00:00
|
|
|
package us.shandian.giga.get;
|
|
|
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.RandomAccessFile;
|
|
|
|
import java.net.HttpURLConnection;
|
|
|
|
import java.net.URL;
|
|
|
|
|
|
|
|
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
|
|
|
|
2017-01-10 10:41:24 +00:00
|
|
|
/**
|
|
|
|
* Runnable to download blocks of a file until the file is completely downloaded,
|
|
|
|
* an error occurs or the process is stopped.
|
|
|
|
*/
|
2016-04-21 23:28:01 +00:00
|
|
|
public class DownloadRunnable implements Runnable
|
|
|
|
{
|
|
|
|
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
|
|
|
|
2017-01-10 10:41:24 +00:00
|
|
|
private final DownloadMission mMission;
|
|
|
|
private final int mId;
|
2016-04-21 23:28:01 +00:00
|
|
|
|
|
|
|
public DownloadRunnable(DownloadMission mission, int id) {
|
2017-01-10 10:41:24 +00:00
|
|
|
if(mission == null) throw new NullPointerException("mission is null");
|
2016-04-21 23:28:01 +00:00
|
|
|
mMission = mission;
|
|
|
|
mId = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
boolean retry = mMission.recovered;
|
|
|
|
long position = mMission.getPosition(mId);
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, mId + ":default pos " + position);
|
|
|
|
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
|
|
|
|
|
|
|
|
if (Thread.currentThread().isInterrupted()) {
|
|
|
|
mMission.pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DEBUG && retry) {
|
|
|
|
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for an unblocked position
|
|
|
|
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, mId + ":position " + position + " preserved, passing");
|
|
|
|
}
|
|
|
|
|
|
|
|
position++;
|
|
|
|
}
|
|
|
|
|
|
|
|
retry = false;
|
|
|
|
|
|
|
|
if (position >= mMission.blocks) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, mId + ":preserving position " + position);
|
|
|
|
}
|
|
|
|
|
|
|
|
mMission.preserveBlock(position);
|
|
|
|
mMission.setPosition(mId, position);
|
|
|
|
|
|
|
|
long start = position * DownloadManager.BLOCK_SIZE;
|
|
|
|
long end = start + DownloadManager.BLOCK_SIZE - 1;
|
|
|
|
|
|
|
|
if (end >= mMission.length) {
|
|
|
|
end = mMission.length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
HttpURLConnection conn = null;
|
|
|
|
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
try {
|
|
|
|
URL url = new URL(mMission.url);
|
|
|
|
conn = (HttpURLConnection) url.openConnection();
|
|
|
|
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
|
|
|
|
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
|
|
|
}
|
|
|
|
|
2017-01-10 10:41:24 +00:00
|
|
|
// A server may be ignoring the range request
|
2016-04-21 23:28:01 +00:00
|
|
|
if (conn.getResponseCode() != 206) {
|
|
|
|
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
|
|
|
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
|
|
|
f.seek(start);
|
|
|
|
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
|
|
|
byte[] buf = new byte[512];
|
|
|
|
|
|
|
|
while (start < end && mMission.running) {
|
|
|
|
int len = ipt.read(buf, 0, 512);
|
|
|
|
|
|
|
|
if (len == -1) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
start += len;
|
|
|
|
total += len;
|
|
|
|
f.write(buf, 0, len);
|
|
|
|
notifyProgress(len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DEBUG && mMission.running) {
|
|
|
|
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
|
|
|
|
}
|
|
|
|
|
|
|
|
f.close();
|
|
|
|
ipt.close();
|
|
|
|
|
|
|
|
// TODO We should save progress for each thread
|
|
|
|
} catch (Exception e) {
|
|
|
|
// TODO Retry count limit & notify error
|
|
|
|
retry = true;
|
|
|
|
|
|
|
|
notifyProgress(-total);
|
|
|
|
|
|
|
|
if (DEBUG) {
|
2017-01-10 10:41:24 +00:00
|
|
|
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
2016-04-21 23:28:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "thread " + mId + " exited main loop");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mMission.errCode == -1 && mMission.running) {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "no error has happened, notifying");
|
|
|
|
}
|
|
|
|
notifyFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DEBUG && !mMission.running) {
|
|
|
|
Log.d(TAG, "The mission has been paused. Passing.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void notifyProgress(final long len) {
|
|
|
|
synchronized (mMission) {
|
|
|
|
mMission.notifyProgress(len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void notifyError(final int err) {
|
|
|
|
synchronized (mMission) {
|
|
|
|
mMission.notifyError(err);
|
|
|
|
mMission.pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void notifyFinished() {
|
|
|
|
synchronized (mMission) {
|
|
|
|
mMission.notifyFinished();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|