package com.example.ipcplayer.download;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ConnectTimeoutException;
import com.example.ipcplayer.utils.LogUtil;
import com.example.ipcplayer.utils.NetworkUtil;
import com.example.ipcplayer.utils.StorageUtil;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadTask extends AsyncTask<Void, Integer, Long> {
public final static int TIME_OUT = 30000;
private final static int BUFFER_SIZE = 1024 * 8;
private static final String TAG = "DownloadTask";
private static final boolean DEBUG = true;
private static final String TEMP_SUFFIX = ".tmp";
private URL mURL;
private File mFile;
private File mTempFile;
private String mUrl;
private RandomAccessFile mOutputStream;
private DownloadListener mListener;
private Context mContext;
private long mDownloadSize;
private long mPreviousFileSize;
private long mTotalSize;
private long mDownloadPercent;
private long mNetworkSpeed;
private long mPreviousTime;
private long mTotalTime;
private Throwable error = null;
private boolean mInterrupt = false;
private DownloadInfo mDownloadInfo;
private final class ProgressReportingRandomAccessFile extends RandomAccessFile {
private int progress = 0;
public ProgressReportingRandomAccessFile(File file, String mode)
throws FileNotFoundException {
super(file, mode);
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
super.write(buffer, offset, count);
progress += count;
publishProgress(progress);
}
}
public DownloadTask(Context context, String url, String path) {
this(context, url, path, null);
}
public DownloadTask(Context context, String mUrl, String path, DownloadListener listener){
super();
this.mUrl = mUrl;
try {
this.mURL = new URL(mUrl);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.mListener = listener;
String fileName = new File(mURL.getFile()).getName();
this.mFile = new File(path, fileName);
this.mTempFile = new File(path, fileName + TEMP_SUFFIX);
this.mContext = context;
}
public String getURL() {
return mUrl;
}
public boolean isInterrupt() {
return mInterrupt;
}
public long getDownloadPercent() {
return mDownloadPercent;
}
public long getDownloadSize() {
return mDownloadSize + mPreviousFileSize;
}
public long getTotalSize() {
return mTotalSize;
}
public long getDownloadSpeed() {
return this.mNetworkSpeed;
}
public long getTotalTime() {
return this.mTotalTime;
}
public DownloadListener getListener() {
return this.mListener;
}
@Override
protected void onPreExecute() {
mPreviousTime = System.currentTimeMillis();
if (mListener != null)
mListener.preDownload(mDownloadInfo);
}
@Override
protected Long doInBackground(Void... params) {
long result = -1;
try {
result = download();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
client.close();
}
}
return result;
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (progress.length > 1) {
mTotalSize = progress[1];
if (mTotalSize == -1) {
if (mListener != null)
mListener.errorDownload(mDownloadInfo);
} else {
}
} else {
mTotalTime = System.currentTimeMillis() - mPreviousTime;
mDownloadSize = progress[0];
mDownloadPercent = (mDownloadSize + mPreviousFileSize) * 100 / mTotalSize;
mNetworkSpeed = mDownloadSize / mTotalTime;
if (mListener != null)
mListener.updateProgress(mDownloadInfo);
}
}
@Override
protected void onPostExecute(Long result) {
if (result == -1 || mInterrupt || error != null) {
if (DEBUG && error != null) {
Log.v(TAG, "Download failed." + error.getMessage());
}
if (mListener != null) {
mListener.errorDownload(mDownloadInfo);
}
return;
}
// finish download
mTempFile.renameTo(mFile);
if (mListener != null)
mListener.finishDownload(mDownloadInfo);
}
@Override
public void onCancelled() {
super.onCancelled();
mInterrupt = true;
}
private AndroidHttpClient client;
private HttpGet httpGet;
private HttpResponse response;
private long download() throws IOException, NetworkErrorException{
if (DEBUG) {
Log.v(TAG, "mTotalSize: " + mTotalSize);
}
/*
* check net work
*/
if (!NetworkUtil.isNetworkAvailable()) {
throw new NetworkErrorException("Network blocked.");
}
/*
* check file length
*/
client = AndroidHttpClient.newInstance("DownloadTask");
httpGet = new HttpGet(mUrl);
response = client.execute(httpGet);
mTotalSize = response.getEntity().getContentLength();
if (mFile.exists() && mTotalSize == mFile.length()) {
LogUtil.d(TAG + "Output file already exists. Skipping download.");
// throw new FileAlreadyExistException("Output file already exists. Skipping download.");
} else if (mTempFile.exists()) {
httpGet.addHeader("Range", "bytes=" + mTempFile.length() + "-");
mPreviousFileSize = mTempFile.length();
client.close();
client = AndroidHttpClient.newInstance("DownloadTask");
response = client.execute(httpGet);
if (DEBUG) {
Log.v(TAG, "File is not complete, download now.");
Log.v(TAG, "File length:" + mTempFile.length() + " mTotalSize:" + mTotalSize);
}
}
/*
* check memory
*/
long storage = StorageUtil.getFreeSpace();
if (DEBUG) {
Log.i(null, "storage:" + storage + " mTotalSize:" + mTotalSize);
}
if (mTotalSize - mTempFile.length() > storage) {
// throw new NoMemoryException("SD card no memory.");
}
/*
* start download
*/
mOutputStream = new ProgressReportingRandomAccessFile(mTempFile, "rw");
publishProgress(0, (int) mTotalSize);
InputStream input = response.getEntity().getContent();
int bytesCopied = copy(input, mOutputStream);
if ((mPreviousFileSize + bytesCopied) != mTotalSize && mTotalSize != -1 && !mInterrupt) {
throw new IOException("Download incomplete: " + bytesCopied + " != " + mTotalSize);
}
if (DEBUG) {
Log.v(TAG, "Download completed successfully.");
}
return bytesCopied;
}
public int copy(InputStream input, RandomAccessFile out) throws IOException,
NetworkErrorException {
if (input == null || out == null) {
return -1;
}
byte[] buffer = new byte[BUFFER_SIZE];
BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
if (DEBUG) {
Log.v(TAG, "length" + out.length());
}
int count = 0, n = 0;
long errorBlockTimePreviousTime = -1, expireTime = 0;
try {
out.seek(out.length());
while (!mInterrupt) {
n = in.read(buffer, 0, BUFFER_SIZE);
if (n == -1) {
break;
}
out.write(buffer, 0, n);
count += n;
/*
* check network
*/
if (!NetworkUtil.isNetworkAvailable()) {
throw new NetworkErrorException("Network blocked.");
}
if (mNetworkSpeed == 0) {
if (errorBlockTimePreviousTime > 0) {
expireTime = System.currentTimeMillis() - errorBlockTimePreviousTime;
if (expireTime > TIME_OUT) {
throw new ConnectTimeoutException("connection time out.");
}
} else {
errorBlockTimePreviousTime = System.currentTimeMillis();
}
} else {
expireTime = 0;
errorBlockTimePreviousTime = -1;
}
}
} finally {
client.close(); // must close client first
client = null;
out.close();
in.close();
input.close();
}
return count;
}
}