* migrate few annotations to androidx * mission recovery: better error handling (except StreamExtractor.getErrorMessage() method always returns an error) * post-processing: more detailed progress [file specific changes] DownloadMission.java * remove redundant/boilerplate code (again) * make few variables volatile * better file "length" approximation * use "done" variable to count the amount of bytes downloaded (simplify percent calc in UI code) Postprocessing.java * if case of error use "ERROR_POSTPROCESSING" instead of "ERROR_UNKNOWN_EXCEPTION" * simplify source stream init DownloadManager.java * move all "service message sending" code to DownloadMission * remove not implemented method "notifyUserPendingDownloads()" also his unused strings DownloadManagerService.java * use START_STICKY instead of START_NOT_STICKY * simplify addMissionEventListener()/removeMissionEventListener() methods (always are called from the main thread) Deleter.java * better method definition MissionAdapter.java * better method definition * code cleanup * the UI is now refreshed every 750ms * simplify download progress calculation * indicates if the download is actually recovering * smooth download speed measure * show estimated remain time MainFragment.java: * check if viewPager is null (issued by "Apply changes" feature of Android Studio)
155 lines
3.3 KiB
Java
155 lines
3.3 KiB
Java
package us.shandian.giga.io;
|
|
|
|
import org.schabi.newpipe.streams.io.SharpStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
public class ChunkFileInputStream extends SharpStream {
|
|
private static final int REPORT_INTERVAL = 256 * 1024;
|
|
|
|
private SharpStream source;
|
|
private final long offset;
|
|
private final long length;
|
|
private long position;
|
|
|
|
private long progressReport;
|
|
private final ProgressReport onProgress;
|
|
|
|
public ChunkFileInputStream(SharpStream target, long start, long end, ProgressReport callback) throws IOException {
|
|
source = target;
|
|
offset = start;
|
|
length = end - start;
|
|
position = 0;
|
|
onProgress = callback;
|
|
progressReport = REPORT_INTERVAL;
|
|
|
|
if (length < 1) {
|
|
source.close();
|
|
throw new IOException("The chunk is empty or invalid");
|
|
}
|
|
if (source.length() < end) {
|
|
try {
|
|
throw new IOException(String.format("invalid file length. expected = %s found = %s", end, source.length()));
|
|
} finally {
|
|
source.close();
|
|
}
|
|
}
|
|
|
|
source.seek(offset);
|
|
}
|
|
|
|
/**
|
|
* Get absolute position on file
|
|
*
|
|
* @return the position
|
|
*/
|
|
public long getFilePointer() {
|
|
return offset + position;
|
|
}
|
|
|
|
@Override
|
|
public int read() throws IOException {
|
|
if ((position + 1) > length) {
|
|
return 0;
|
|
}
|
|
|
|
int res = source.read();
|
|
if (res >= 0) {
|
|
position++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Override
|
|
public int read(byte[] b) throws IOException {
|
|
return read(b, 0, b.length);
|
|
}
|
|
|
|
@Override
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
if ((position + len) > length) {
|
|
len = (int) (length - position);
|
|
}
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
int res = source.read(b, off, len);
|
|
position += res;
|
|
|
|
if (onProgress != null && position > progressReport) {
|
|
onProgress.report(position);
|
|
progressReport = position + REPORT_INTERVAL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Override
|
|
public long skip(long pos) throws IOException {
|
|
pos = Math.min(pos + position, length);
|
|
|
|
if (pos == 0) {
|
|
return 0;
|
|
}
|
|
|
|
source.seek(offset + pos);
|
|
|
|
long oldPos = position;
|
|
position = pos;
|
|
|
|
return pos - oldPos;
|
|
}
|
|
|
|
@Override
|
|
public long available() {
|
|
return (int) (length - position);
|
|
}
|
|
|
|
@SuppressWarnings("EmptyCatchBlock")
|
|
@Override
|
|
public void close() {
|
|
source.close();
|
|
source = null;
|
|
}
|
|
|
|
@Override
|
|
public boolean isClosed() {
|
|
return source == null;
|
|
}
|
|
|
|
@Override
|
|
public void rewind() throws IOException {
|
|
position = 0;
|
|
source.seek(offset);
|
|
}
|
|
|
|
@Override
|
|
public boolean canRewind() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean canRead() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean canWrite() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void write(byte value) {
|
|
}
|
|
|
|
@Override
|
|
public void write(byte[] buffer) {
|
|
}
|
|
|
|
@Override
|
|
public void write(byte[] buffer, int offset, int count) {
|
|
}
|
|
|
|
}
|