findAllDownloaded();
29 |
30 | DownloadDBController getDownloadDBController();
31 |
32 | void resumeAll();
33 |
34 | void pauseAll();
35 |
36 | void onDownloadFailed(DownloadInfo downloadInfo);
37 | }
38 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/config/Config.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.config;
2 |
3 | import com.ixuea.android.downloader.db.DownloadDBController;
4 |
5 | /**
6 | * Download manager config.
7 | *
8 | * Can configure Timeout,Concurrent downloads task number, Each Download thread number
9 | *
10 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
11 | */
12 | public class Config {
13 |
14 | private final String method = "GET";
15 | private int connectTimeout = 10000;
16 | private int readTimeout = 10000;
17 | private int downloadThread = 2;
18 | private int eachDownloadThread = 2;
19 | private String databaseName = "download_info.db";
20 | // private String databaseName = "/sdcard/d/download_info.db";
21 | private int databaseVersion = 2;
22 | private int retryDownloadCount = 2;
23 | private DownloadDBController downloadDBController;
24 |
25 | public int getConnectTimeout() {
26 | return connectTimeout;
27 | }
28 |
29 | public void setConnectTimeout(int connectTimeout) {
30 | this.connectTimeout = connectTimeout;
31 | }
32 |
33 | public int getReadTimeout() {
34 | return readTimeout;
35 | }
36 |
37 | public void setReadTimeout(int readTimeout) {
38 | this.readTimeout = readTimeout;
39 | }
40 |
41 | public int getDownloadThread() {
42 | return downloadThread;
43 | }
44 |
45 | public void setDownloadThread(int downloadThread) {
46 | this.downloadThread = downloadThread;
47 | }
48 |
49 | public int getEachDownloadThread() {
50 | return eachDownloadThread;
51 | }
52 |
53 | public void setEachDownloadThread(int eachDownloadThread) {
54 | this.eachDownloadThread = eachDownloadThread;
55 | }
56 |
57 | public String getMethod() {
58 | return method;
59 | }
60 |
61 | public String getDatabaseName() {
62 | return databaseName;
63 | }
64 |
65 | public void setDatabaseName(String databaseName) {
66 | this.databaseName = databaseName;
67 | }
68 |
69 | public int getDatabaseVersion() {
70 | return databaseVersion;
71 | }
72 |
73 | public void setDatabaseVersion(int databaseVersion) {
74 | this.databaseVersion = databaseVersion;
75 | }
76 |
77 | public int getRetryDownloadCount() {
78 | return retryDownloadCount;
79 | }
80 |
81 | public void setRetryDownloadCount(int retryDownloadCount) {
82 | this.retryDownloadCount = retryDownloadCount;
83 | }
84 |
85 | public DownloadDBController getDownloadDBController() {
86 | return downloadDBController;
87 | }
88 |
89 | public void setDownloadDBController(
90 | DownloadDBController downloadDBController) {
91 | this.downloadDBController = downloadDBController;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/DownloadResponse.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core;
2 |
3 | import com.ixuea.android.downloader.domain.DownloadInfo;
4 | import com.ixuea.android.downloader.exception.DownloadException;
5 |
6 | /**
7 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
8 | */
9 |
10 | public interface DownloadResponse {
11 |
12 | void onStatusChanged(DownloadInfo downloadInfo);
13 |
14 | void handleException(DownloadInfo downloadInfo, DownloadException exception);
15 | }
16 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/DownloadResponseImpl.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.Message;
6 | import android.util.Log;
7 |
8 | import com.ixuea.android.downloader.callback.DownloadManager;
9 | import com.ixuea.android.downloader.db.DownloadDBController;
10 | import com.ixuea.android.downloader.domain.DownloadInfo;
11 | import com.ixuea.android.downloader.domain.DownloadThreadInfo;
12 | import com.ixuea.android.downloader.exception.DownloadException;
13 |
14 | /**
15 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
16 | */
17 |
18 | public class DownloadResponseImpl implements DownloadResponse {
19 |
20 | private static final String TAG = "DownloadResponseImpl";
21 | private final Handler handler;
22 | private final DownloadDBController downloadDBController;
23 | private final DownloadManager downloadManager;
24 |
25 | public DownloadResponseImpl(DownloadManager downloadManager,DownloadDBController downloadDBController) {
26 | this.downloadManager = downloadManager;
27 | this.downloadDBController = downloadDBController;
28 |
29 | handler = new Handler(Looper.getMainLooper()) {
30 | @Override
31 | public void handleMessage(Message msg) {
32 | super.handleMessage(msg);
33 | DownloadInfo downloadInfo = (DownloadInfo) msg.obj;
34 | switch (downloadInfo.getStatus()) {
35 | case DownloadInfo.STATUS_DOWNLOADING:
36 | if (downloadInfo.getDownloadListener() != null) {
37 | downloadInfo.getDownloadListener()
38 | .onDownloading(downloadInfo.getProgress(), downloadInfo.getSize());
39 | }
40 |
41 | break;
42 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
43 | if (downloadInfo.getDownloadListener() != null) {
44 | downloadInfo.getDownloadListener().onStart();
45 | }
46 | break;
47 | case DownloadInfo.STATUS_WAIT:
48 | if (downloadInfo.getDownloadListener() != null) {
49 | downloadInfo.getDownloadListener().onWaited();
50 | }
51 | break;
52 | case DownloadInfo.STATUS_PAUSED:
53 | if (downloadInfo.getDownloadListener() != null) {
54 | downloadInfo.getDownloadListener().onPaused();
55 | }
56 | break;
57 | case DownloadInfo.STATUS_COMPLETED:
58 | if (downloadInfo.getDownloadListener() != null) {
59 | downloadInfo.getDownloadListener().onDownloadSuccess();
60 | }
61 | //TODO submit next downloadInfo task
62 |
63 | break;
64 | case DownloadInfo.STATUS_ERROR:
65 | if (downloadInfo.getDownloadListener() != null) {
66 | downloadInfo.getDownloadListener().onDownloadFailed(downloadInfo.getException());
67 | }
68 | break;
69 | case DownloadInfo.STATUS_REMOVED:
70 | if (downloadInfo.getDownloadListener() != null) {
71 | downloadInfo.getDownloadListener().onRemoved();
72 | }
73 | break;
74 | }
75 | }
76 | };
77 |
78 |
79 | }
80 |
81 | @Override
82 | public void onStatusChanged(DownloadInfo downloadInfo) {
83 | createOrUpdateDownloadInfo(downloadInfo);
84 |
85 | Message message = handler.obtainMessage(downloadInfo.getId().hashCode());
86 | message.obj = downloadInfo;
87 | message.sendToTarget();
88 |
89 | Log.d(TAG, "progress:" + downloadInfo.getProgress() + ",size:" + downloadInfo.getSize());
90 | }
91 |
92 | private void createOrUpdateDownloadInfo(DownloadInfo downloadInfo) {
93 | if (downloadInfo.getStatus() != DownloadInfo.STATUS_REMOVED) {
94 | downloadDBController.createOrUpdate(downloadInfo);
95 | if (downloadInfo.getDownloadThreadInfos() != null) {
96 | for (DownloadThreadInfo threadInfo : downloadInfo.getDownloadThreadInfos()) {
97 | downloadDBController.createOrUpdate(threadInfo);
98 | }
99 | }
100 | }
101 | }
102 |
103 | @Override
104 | public void handleException(DownloadInfo downloadInfo, DownloadException exception) {
105 | downloadInfo.setStatus(DownloadInfo.STATUS_ERROR);
106 | downloadInfo.setException(exception);
107 |
108 | createOrUpdateDownloadInfo(downloadInfo);
109 |
110 | Message message = handler.obtainMessage(downloadInfo.getId().hashCode());
111 | message.obj = downloadInfo;
112 | message.sendToTarget();
113 |
114 | Log.e(TAG, "handleException:" + exception.getLocalizedMessage());
115 |
116 | //下载下一个文件
117 | downloadManager.onDownloadFailed(downloadInfo);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/DownloadTaskImpl.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core;
2 |
3 | import com.ixuea.android.downloader.config.Config;
4 | import com.ixuea.android.downloader.core.task.DownloadTask;
5 | import com.ixuea.android.downloader.core.task.GetFileInfoTask;
6 | import com.ixuea.android.downloader.core.task.GetFileInfoTask.OnGetFileInfoListener;
7 | import com.ixuea.android.downloader.core.thread.DownloadThread;
8 | import com.ixuea.android.downloader.core.thread.DownloadThread.DownloadProgressListener;
9 | import com.ixuea.android.downloader.domain.DownloadInfo;
10 | import com.ixuea.android.downloader.domain.DownloadThreadInfo;
11 | import com.ixuea.android.downloader.exception.DownloadException;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.util.concurrent.ExecutorService;
16 | import java.util.concurrent.atomic.AtomicBoolean;
17 |
18 | /**
19 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
20 | */
21 |
22 | public class DownloadTaskImpl implements DownloadTask, OnGetFileInfoListener,
23 | DownloadProgressListener {
24 |
25 | private final ExecutorService executorService;
26 | private final DownloadResponse downloadResponse;
27 | private final DownloadInfo downloadInfo;
28 | private final Config config;
29 | private final List downloadThreads;
30 | private final DownloadTaskListener downloadTaskListener;
31 | private long lastRefreshTime = System.currentTimeMillis();
32 | private long progress;
33 | private volatile AtomicBoolean isComputerDownload = new AtomicBoolean(false);
34 | // private volatile boolean isComputerDownload;
35 |
36 | public DownloadTaskImpl(ExecutorService executorService, DownloadResponse downloadResponse,
37 | DownloadInfo downloadInfo, Config config, DownloadTaskListener downloadTaskListener) {
38 | this.executorService = executorService;
39 | this.downloadResponse = downloadResponse;
40 | this.downloadInfo = downloadInfo;
41 | this.config = config;
42 | this.downloadTaskListener = downloadTaskListener;
43 | this.downloadThreads = new ArrayList<>();
44 |
45 | }
46 |
47 | @Override
48 | public void start() {
49 | if (downloadInfo.getSize() <= 0) {
50 | //get downloadInfo info size
51 | getFileInfo();
52 | } else {
53 | List downloadThreadInfos = downloadInfo.getDownloadThreadInfos();
54 | for (DownloadThreadInfo downloadThreadInfo : downloadThreadInfos
55 | ) {
56 | DownloadThread downloadThread = new DownloadThread(downloadThreadInfo, downloadResponse,
57 | config,
58 | downloadInfo, this);
59 | executorService.submit(downloadThread);
60 | downloadThreads.add(downloadThread);
61 | }
62 |
63 | downloadInfo.setStatus(DownloadInfo.STATUS_DOWNLOADING);
64 | downloadResponse.onStatusChanged(downloadInfo);
65 | }
66 | }
67 |
68 | private void getFileInfo() {
69 | GetFileInfoTask getFileInfoTask = new GetFileInfoTask(downloadResponse, downloadInfo, this);
70 | executorService.submit(getFileInfoTask);
71 | }
72 |
73 | @Override
74 | public void onSuccess(long size, boolean isSupportRanges) {
75 | downloadInfo.setSupportRanges(isSupportRanges);
76 | downloadInfo.setSize(size);
77 |
78 | List downloadThreadInfos = new ArrayList<>();
79 | if (isSupportRanges) {
80 | long length = downloadInfo.getSize();
81 | final int threads = config.getEachDownloadThread();
82 | final long average = length / threads;
83 | for (int i = 0; i < threads; i++) {
84 | long start = average * i;
85 | long end;
86 | if (i == threads - 1) {
87 | end = length-1;
88 | } else {
89 | end = start + average - 1;
90 | }
91 | DownloadThreadInfo downloadThreadInfo = new DownloadThreadInfo(i, downloadInfo.getId(),
92 | downloadInfo
93 | .getUri(), start,
94 | end);
95 | downloadThreadInfos.add(downloadThreadInfo);
96 |
97 | DownloadThread downloadThread = new DownloadThread(downloadThreadInfo, downloadResponse,
98 | config,
99 | downloadInfo, this);
100 | executorService.submit(downloadThread);
101 | downloadThreads.add(downloadThread);
102 | }
103 | } else {
104 | DownloadThreadInfo downloadThreadInfo = new DownloadThreadInfo(0, downloadInfo.getId(),
105 | downloadInfo
106 | .getUri(), 0,
107 | downloadInfo.getSize());
108 | downloadThreadInfos.add(downloadThreadInfo);
109 |
110 | DownloadThread downloadThread = new DownloadThread(downloadThreadInfo, downloadResponse,
111 | config,
112 | downloadInfo, this);
113 | executorService.submit(downloadThread);
114 | downloadThreads.add(downloadThread);
115 | }
116 | downloadInfo.setDownloadThreadInfos(downloadThreadInfos);
117 | downloadInfo.setStatus(DownloadInfo.STATUS_DOWNLOADING);
118 | downloadResponse.onStatusChanged(downloadInfo);
119 |
120 | }
121 |
122 | @Override
123 | public void onFailed(DownloadException exception) {
124 |
125 | }
126 |
127 | @Override
128 | public void onProgress() {
129 | if (!isComputerDownload.get()) {
130 | synchronized (this) {
131 | if (!isComputerDownload.get()) {
132 | isComputerDownload.set(true);
133 | long currentTimeMillis = System.currentTimeMillis();
134 | if ((currentTimeMillis - lastRefreshTime) > 1000) {
135 | computerDownloadProgress();
136 | downloadResponse.onStatusChanged(downloadInfo);
137 | lastRefreshTime = currentTimeMillis;
138 | }
139 | isComputerDownload.set(false);
140 | }
141 | }
142 | }
143 |
144 | }
145 |
146 | @Override
147 | public void onDownloadSuccess() {
148 | computerDownloadProgress();
149 | if (downloadInfo.getProgress() == downloadInfo.getSize()) {
150 | downloadInfo.setStatus(DownloadInfo.STATUS_COMPLETED);
151 | downloadResponse.onStatusChanged(downloadInfo);
152 | if (downloadTaskListener != null) {
153 | downloadTaskListener.onDownloadSuccess(downloadInfo);
154 | }
155 | }
156 | }
157 |
158 | private void computerDownloadProgress() {
159 | progress = 0;
160 | List downloadThreadInfos = downloadInfo.getDownloadThreadInfos();
161 | for (DownloadThreadInfo info : downloadThreadInfos) {
162 | progress += info.getProgress();
163 | }
164 | downloadInfo.setProgress(progress);
165 |
166 | }
167 |
168 | /***
169 | * Download task listener.
170 | *
171 | * Use in download thread
172 | */
173 | public interface DownloadTaskListener {
174 |
175 | void onDownloadSuccess(DownloadInfo downloadInfo);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/task/DownloadTask.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core.task;
2 |
3 | /**
4 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
5 | */
6 |
7 | public interface DownloadTask {
8 |
9 | void start();
10 | }
11 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/task/GetFileInfoTask.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core.task;
2 |
3 | import android.os.Process;
4 | import android.text.TextUtils;
5 |
6 | import com.ixuea.android.downloader.core.DownloadResponse;
7 | import com.ixuea.android.downloader.domain.DownloadInfo;
8 | import com.ixuea.android.downloader.exception.DownloadException;
9 |
10 | import java.io.IOException;
11 | import java.net.HttpURLConnection;
12 | import java.net.MalformedURLException;
13 | import java.net.ProtocolException;
14 | import java.net.URL;
15 |
16 | import static com.ixuea.android.downloader.exception.DownloadException.EXCEPTION_OTHER;
17 |
18 | /**
19 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
20 | */
21 |
22 | public class GetFileInfoTask implements Runnable {
23 |
24 | private final DownloadResponse downloadResponse;
25 | private final DownloadInfo downloadInfo;
26 | private final OnGetFileInfoListener onGetFileInfoListener;
27 |
28 | public GetFileInfoTask(DownloadResponse downloadResponse, DownloadInfo downloadInfo,
29 | OnGetFileInfoListener onGetFileInfoListener) {
30 | this.downloadResponse = downloadResponse;
31 | this.downloadInfo = downloadInfo;
32 | this.onGetFileInfoListener = onGetFileInfoListener;
33 | }
34 |
35 | @Override
36 | public void run() {
37 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
38 | try {
39 | executeConnection();
40 | } catch (DownloadException e) {
41 | downloadResponse.handleException(downloadInfo, e);
42 | } catch (Exception e) {
43 | downloadResponse.handleException(downloadInfo, new DownloadException(EXCEPTION_OTHER, e));
44 | }
45 | }
46 |
47 | private void executeConnection() throws DownloadException {
48 | HttpURLConnection httpConnection = null;
49 |
50 | final URL url;
51 | try {
52 | url = new URL(downloadInfo.getUri());
53 | httpConnection = (HttpURLConnection) url.openConnection();
54 | httpConnection.setConnectTimeout(10000);
55 | httpConnection.setReadTimeout(10000);
56 | httpConnection.setRequestMethod("GET");
57 | httpConnection.setRequestProperty("Range", "bytes=" + 0 + "-");
58 | final int responseCode = httpConnection.getResponseCode();
59 | if (responseCode == HttpURLConnection.HTTP_OK) {
60 | parseHttpResponse(httpConnection, false);
61 | } else if (responseCode == HttpURLConnection.HTTP_PARTIAL) {
62 | parseHttpResponse(httpConnection, true);
63 | } else {
64 | throw new DownloadException(DownloadException.EXCEPTION_SERVER_ERROR,
65 | "UnSupported response code:" + responseCode);
66 | }
67 | } catch (MalformedURLException e) {
68 | throw new DownloadException(DownloadException.EXCEPTION_URL_ERROR, "Bad url.", e);
69 | } catch (ProtocolException e) {
70 | throw new DownloadException(DownloadException.EXCEPTION_PROTOCOL, "Protocol error", e);
71 | } catch (IOException e) {
72 | throw new DownloadException(DownloadException.EXCEPTION_IO_EXCEPTION, "IO error", e);
73 | } catch (Exception e) {
74 | throw new DownloadException(DownloadException.EXCEPTION_IO_EXCEPTION, "Unknown error", e);
75 | } finally {
76 | // if (httpConnection != null) {
77 | // httpConnection.disconnect();
78 | // }
79 | }
80 | }
81 |
82 | private void parseHttpResponse(HttpURLConnection httpConnection, boolean isAcceptRanges)
83 | throws DownloadException {
84 |
85 | final long length;
86 | String contentLength = httpConnection.getHeaderField("Content-Length");
87 | if (TextUtils.isEmpty(contentLength) || contentLength.equals("0") || contentLength
88 | .equals("-1")) {
89 | length = httpConnection.getContentLength();
90 | } else {
91 | length = Long.parseLong(contentLength);
92 | }
93 |
94 | if (length <= 0) {
95 | throw new DownloadException(DownloadException.EXCEPTION_FILE_SIZE_ZERO, "length <= 0");
96 | }
97 |
98 | checkIfPause();
99 |
100 | onGetFileInfoListener.onSuccess(length, isAcceptRanges);
101 | }
102 |
103 | private void checkIfPause() {
104 | if (downloadInfo.isPause()) {
105 | throw new DownloadException(DownloadException.EXCEPTION_PAUSE);
106 | }
107 | }
108 |
109 | /**
110 | * Get file info listener.
111 | */
112 | public interface OnGetFileInfoListener {
113 |
114 | void onSuccess(long size, boolean isSupportRanges);
115 |
116 | void onFailed(DownloadException exception);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/core/thread/DownloadThread.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.core.thread;
2 |
3 |
4 | import android.os.Process;
5 | import android.util.Log;
6 |
7 | import com.ixuea.android.downloader.config.Config;
8 | import com.ixuea.android.downloader.core.DownloadResponse;
9 | import com.ixuea.android.downloader.domain.DownloadInfo;
10 | import com.ixuea.android.downloader.domain.DownloadThreadInfo;
11 | import com.ixuea.android.downloader.exception.DownloadException;
12 | import com.ixuea.android.downloader.exception.DownloadPauseException;
13 |
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.RandomAccessFile;
17 | import java.net.HttpURLConnection;
18 | import java.net.ProtocolException;
19 | import java.net.URL;
20 |
21 | /**
22 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
23 | */
24 |
25 | public class DownloadThread implements Runnable {
26 |
27 | public static final String TAG = "DownloadThread";
28 |
29 | private final DownloadThreadInfo downloadThreadInfo;
30 | private final DownloadResponse downloadResponse;
31 | private final Config config;
32 | private final DownloadInfo downloadInfo;
33 | private final DownloadProgressListener downloadProgressListener;
34 | private long lastProgress;
35 | private InputStream inputStream;
36 | private int retryDownloadCount = 0;
37 |
38 | public DownloadThread(DownloadThreadInfo downloadThreadInfo, DownloadResponse downloadResponse,
39 | Config config,
40 | DownloadInfo downloadInfo, DownloadProgressListener downloadProgressListener) {
41 | this.downloadThreadInfo = downloadThreadInfo;
42 | this.downloadResponse = downloadResponse;
43 | this.config = config;
44 | this.downloadInfo = downloadInfo;
45 | this.lastProgress = downloadThreadInfo.getProgress();
46 | this.downloadProgressListener = downloadProgressListener;
47 | }
48 |
49 | @Override
50 | public void run() {
51 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
52 | // while (!(downloadInfo.isPause() || downloadThreadInfo.isThreadDownloadSuccess())) {
53 |
54 | checkPause();
55 | try {
56 | executeDownload();
57 | } catch (DownloadException e) {
58 |
59 | // if (retryDownloadCount >= config.getRetryDownloadCount()) {
60 | downloadResponse.handleException(downloadInfo,e);
61 | // }
62 | //
63 | // retryDownloadCount++;
64 | }
65 | // checkPause();
66 | // }
67 | }
68 |
69 | private void executeDownload() {
70 | HttpURLConnection httpConnection = null;
71 | try {
72 | final URL url = new URL(downloadThreadInfo.getUri());
73 | httpConnection = (HttpURLConnection) url.openConnection();
74 | httpConnection.setConnectTimeout(config.getConnectTimeout());
75 | httpConnection.setReadTimeout(config.getReadTimeout());
76 | httpConnection.setRequestMethod(config.getMethod());
77 | long lastStart = downloadThreadInfo.getStart() + lastProgress;
78 | if (downloadInfo.isSupportRanges()) {
79 | httpConnection.setRequestProperty("Range",
80 | "bytes=" + lastStart + "-" + downloadThreadInfo.getEnd());
81 | }
82 | final int responseCode = httpConnection.getResponseCode();
83 | if (responseCode == HttpURLConnection.HTTP_PARTIAL
84 | || responseCode == HttpURLConnection.HTTP_OK) {
85 | inputStream = httpConnection.getInputStream();
86 | RandomAccessFile raf = new RandomAccessFile(downloadInfo.getPath(), "rwd");
87 |
88 | raf.seek(lastStart);
89 | final byte[] bf = new byte[1024 * 4];
90 | int len = -1;
91 | int offset = 0;
92 | while (true) {
93 | checkPause();
94 | len = inputStream.read(bf);
95 | if (len == -1) {
96 | break;
97 | }
98 | raf.write(bf, 0, len);
99 | offset += len;
100 |
101 | // synchronized (downloadProgressListener) {
102 | downloadThreadInfo.setProgress(lastProgress + offset);
103 | downloadProgressListener.onProgress();
104 | // }
105 |
106 | Log.d(TAG,
107 | "downloadInfo:" + downloadInfo.getId() + " thread:" + downloadThreadInfo.getThreadId()
108 | + " progress:"
109 | + downloadThreadInfo.getProgress()
110 | + ",start:" + downloadThreadInfo.getStart() + ",end:" + downloadThreadInfo
111 | .getEnd());
112 | }
113 |
114 | //downloadInfo success
115 | downloadProgressListener.onDownloadSuccess();
116 | } else {
117 | throw new DownloadException(DownloadException.EXCEPTION_SERVER_SUPPORT_CODE,
118 | "UnSupported response code:" + responseCode);
119 | }
120 | checkPause();
121 | } catch (ProtocolException e) {
122 | throw new DownloadException(DownloadException.EXCEPTION_PROTOCOL, "Protocol error", e);
123 | } catch (IOException e) {
124 | throw new DownloadException(DownloadException.EXCEPTION_IO_EXCEPTION, "IO error", e);
125 | } catch (DownloadPauseException e) {
126 | //TODO process pause logic
127 | } catch (Exception e) {
128 | throw new DownloadException(DownloadException.EXCEPTION_OTHER, "other error", e);
129 | } finally {
130 | if (httpConnection != null) {
131 | httpConnection.disconnect();
132 | }
133 | }
134 | }
135 |
136 | private void checkPause() {
137 | if (downloadInfo.isPause()) {
138 | throw new DownloadPauseException(DownloadException.EXCEPTION_PAUSE);
139 | }
140 | }
141 |
142 | /**
143 | * Download thread progress listener.
144 | */
145 | public interface DownloadProgressListener {
146 |
147 | void onProgress();
148 |
149 | void onDownloadSuccess();
150 | }
151 |
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/db/DefaultDownloadDBController.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.db;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.database.sqlite.SQLiteDatabase;
6 |
7 | import com.ixuea.android.downloader.config.Config;
8 | import com.ixuea.android.downloader.domain.DownloadInfo;
9 | import com.ixuea.android.downloader.domain.DownloadThreadInfo;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_COMPLETED;
15 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_PAUSED;
16 |
17 | /**
18 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
19 | */
20 |
21 | public final class DefaultDownloadDBController implements DownloadDBController {
22 |
23 |
24 | public static final String[] DOWNLOAD_INFO_COLUMNS = new String[]{"_id", "supportRanges",
25 | "createAt", "uri",
26 | "path", "size", "progress",
27 | "status"};
28 |
29 | public static final String[] DOWNLOAD_THREAD_INFO_COLUMNS = new String[]{"_id", "threadId",
30 | "downloadInfoId", "uri",
31 | "start", "end", "progress"};
32 | public static final String SQL_UPDATE_DOWNLOAD_THREAD_INFO = String.format(
33 | "REPLACE INTO %s (_id,threadId,downloadInfoId,uri,start,end,progress) VALUES(?,?,?,?,?,?,?);",
34 | DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_THREAD_INFO);
35 |
36 | public static final String SQL_UPDATE_DOWNLOAD_INFO = String.format(
37 | "REPLACE INTO %s (_id,supportRanges,createAt,uri,path,size,progress,status) VALUES(?,?,?,?,?,?,?,?);",
38 | DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO);
39 |
40 | public static final String SQL_UPDATE_DOWNLOADING_INFO_STATUS = String.format(
41 | "UPDATE %s SET status=? WHERE status!=?;",
42 | DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO);
43 |
44 | private final Context context;
45 | private final DefaultDownloadHelper dbHelper;
46 | private final SQLiteDatabase writableDatabase;
47 | private final SQLiteDatabase readableDatabase;
48 |
49 | public DefaultDownloadDBController(Context context, Config config) {
50 | this.context = context;
51 | dbHelper = new DefaultDownloadHelper(context, config);
52 | writableDatabase = dbHelper.getWritableDatabase();
53 | readableDatabase = dbHelper.getReadableDatabase();
54 | }
55 |
56 | @SuppressWarnings("No problem")
57 | @Override
58 | public List findAllDownloading() {
59 | Cursor cursor = readableDatabase.query(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO,
60 | DOWNLOAD_INFO_COLUMNS, "status!=?", new String[]{
61 | String.valueOf(STATUS_COMPLETED)}, null, null, "createAt desc");
62 |
63 | List downloads = new ArrayList<>();
64 | Cursor downloadCursor;
65 | while (cursor.moveToNext()) {
66 | DownloadInfo downloadInfo = new DownloadInfo();
67 | downloads.add(downloadInfo);
68 |
69 | inflateDownloadInfo(cursor, downloadInfo);
70 |
71 | //query download thread info
72 | downloadCursor = readableDatabase.query(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_THREAD_INFO,
73 | DOWNLOAD_THREAD_INFO_COLUMNS, "downloadInfoId=?", new String[]{
74 | String.valueOf(downloadInfo.getId())}, null, null, null);
75 | List downloadThreads = new ArrayList<>();
76 | while (downloadCursor.moveToNext()) {
77 | DownloadThreadInfo downloadThreadInfo = new DownloadThreadInfo();
78 | downloadThreads.add(downloadThreadInfo);
79 | inflateDownloadThreadInfo(downloadCursor, downloadThreadInfo);
80 | }
81 |
82 | downloadInfo.setDownloadThreadInfos(downloadThreads);
83 |
84 | }
85 | return downloads;
86 | }
87 |
88 | @Override
89 | public List findAllDownloaded() {
90 | Cursor cursor = readableDatabase.query(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO,
91 | DOWNLOAD_INFO_COLUMNS, "status=?", new String[]{
92 | String.valueOf(STATUS_COMPLETED)}, null, null, "createAt desc");
93 |
94 | List downloads = new ArrayList<>();
95 | while (cursor.moveToNext()) {
96 | DownloadInfo downloadInfo = new DownloadInfo();
97 | downloads.add(downloadInfo);
98 | inflateDownloadInfo(cursor, downloadInfo);
99 | }
100 | return downloads;
101 | }
102 |
103 | private void inflateDownloadThreadInfo(Cursor cursor,
104 | DownloadThreadInfo downloadThreadInfo) {
105 | downloadThreadInfo.setId(cursor.getInt(0));
106 | downloadThreadInfo.setThreadId(cursor.getInt(1));
107 | downloadThreadInfo.setDownloadInfoId(cursor.getString(2));
108 | downloadThreadInfo.setUri(cursor.getString(3));
109 | downloadThreadInfo.setStart(cursor.getLong(4));
110 | downloadThreadInfo.setEnd(cursor.getLong(5));
111 | downloadThreadInfo.setProgress(cursor.getLong(6));
112 | }
113 |
114 | private void inflateDownloadInfo(Cursor cursor, DownloadInfo downloadInfo) {
115 | downloadInfo.setId(cursor.getString(0));
116 | downloadInfo.setSupportRanges(cursor.getInt(1));
117 | downloadInfo.setCreateAt(cursor.getLong(2));
118 | downloadInfo.setUri(cursor.getString(3));
119 | downloadInfo.setPath(cursor.getString(4));
120 | downloadInfo.setSize(cursor.getLong(5));
121 | downloadInfo.setProgress(cursor.getLong(6));
122 | downloadInfo.setStatus(cursor.getInt(7));
123 | }
124 |
125 | @Override
126 | public DownloadInfo findDownloadedInfoById(String id) {
127 | Cursor cursor = readableDatabase
128 | .query(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO, DOWNLOAD_INFO_COLUMNS, "_id=?",
129 | new String[]{id},
130 | null, null, "createAt desc");
131 | if (cursor.moveToNext()) {
132 | DownloadInfo downloadInfo = new DownloadInfo();
133 |
134 | inflateDownloadInfo(cursor, downloadInfo);
135 |
136 | return downloadInfo;
137 | }
138 | return null;
139 | }
140 |
141 | @Override
142 | public void pauseAllDownloading() {
143 | writableDatabase.execSQL(
144 | SQL_UPDATE_DOWNLOADING_INFO_STATUS,
145 | new Object[]{STATUS_PAUSED, STATUS_COMPLETED});
146 | }
147 |
148 | @Override
149 | public void createOrUpdate(DownloadInfo downloadInfo) {
150 | writableDatabase.execSQL(
151 | SQL_UPDATE_DOWNLOAD_INFO,
152 | new Object[]{
153 | downloadInfo.getId(), downloadInfo.getSupportRanges(),
154 | downloadInfo.getCreateAt(), downloadInfo.getUri(), downloadInfo.getPath(),
155 | downloadInfo.getSize(), downloadInfo.getProgress(), downloadInfo.getStatus()});
156 | }
157 |
158 | @Override
159 | public void createOrUpdate(DownloadThreadInfo downloadThreadInfo) {
160 | writableDatabase.execSQL(
161 | SQL_UPDATE_DOWNLOAD_THREAD_INFO,
162 | new Object[]{
163 | downloadThreadInfo.getId(),
164 | downloadThreadInfo.getThreadId(),
165 | downloadThreadInfo.getDownloadInfoId(),
166 | downloadThreadInfo.getUri(),
167 | downloadThreadInfo.getStart(), downloadThreadInfo.getEnd(),
168 | downloadThreadInfo.getProgress()});
169 | }
170 |
171 | @Override
172 | public void delete(DownloadInfo downloadInfo) {
173 | writableDatabase.delete(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_INFO, "_id=?",
174 | new String[]{String.valueOf(downloadInfo.getId())});
175 | writableDatabase
176 | .delete(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_THREAD_INFO, "downloadInfoId=?",
177 | new String[]{String.valueOf(downloadInfo.getId())});
178 | }
179 |
180 | @Override
181 | public void delete(DownloadThreadInfo downloadThreadInfo) {
182 | writableDatabase
183 | .delete(DefaultDownloadHelper.TABLE_NAME_DOWNLOAD_THREAD_INFO, "id=?",
184 | new String[]{String.valueOf(downloadThreadInfo.getId())});
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/db/DefaultDownloadHelper.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.db;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 | import com.ixuea.android.downloader.config.Config;
8 |
9 | /**
10 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
11 | */
12 |
13 | public class DefaultDownloadHelper extends SQLiteOpenHelper {
14 |
15 | public static final String TABLE_NAME_DOWNLOAD_INFO = "download_info";
16 | public static final String TABLE_NAME_DOWNLOAD_THREAD_INFO = "download_thread_info";
17 | private static final String SQL_CREATE_DOWNLOAD_TABLE = String.format(
18 | "CREATE TABLE %s (_id varchar(255) PRIMARY KEY NOT NULL,supportRanges integer NOT NULL,createAt long NOT NULL,uri varchar(255) NOT NULL,path varchar(255) NOT NULL,size long NOT NULL, progress long NOT NULL,status integer NOT NULL);",
19 | TABLE_NAME_DOWNLOAD_INFO);
20 | private static final String SQL_CREATE_DOWNLOAD_THREAD_TABLE = String.format(
21 | "CREATE TABLE %s (_id integer PRIMARY KEY NOT NULL,threadId integer NOT NULL,downloadInfoId varchar(255) NOT NULL,uri varchar(255) NOT NULL,start long NOT NULL,end long NOT NULL,progress long NOT NULL);",
22 | TABLE_NAME_DOWNLOAD_THREAD_INFO);
23 | private static final int DB_VERSION = 1;
24 |
25 |
26 | public DefaultDownloadHelper(Context context, Config config) {
27 | super(context, config.getDatabaseName(), null, config.getDatabaseVersion());
28 | }
29 |
30 | @Override
31 | public void onCreate(SQLiteDatabase db) {
32 | createTable(db);
33 | }
34 |
35 | private void createTable(SQLiteDatabase db) {
36 | db.execSQL(SQL_CREATE_DOWNLOAD_TABLE);
37 | db.execSQL(SQL_CREATE_DOWNLOAD_THREAD_TABLE);
38 | }
39 |
40 | @Override
41 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
42 | //TODO upgrade database
43 |
44 | }
45 |
46 | // CREATE TABLE download_info (
47 | // _id integer PRIMARY KEY NOT NULL,
48 | // id varchar(255) NOT NULL,
49 | // supportRanges integer NOT NULL,
50 | // createAt long NOT NULL,
51 | // url varchar(255) NOT NULL,
52 | // path varchar(255) NOT NULL,
53 | // size long NOT NULL,
54 | // progress long NOT NULL,
55 | // status integer NOT NULL
56 | // );
57 |
58 | // CREATE TABLE download_thread (
59 | // _id integer PRIMARY KEY NOT NULL,
60 | // downloadkey integer NOT NULL,
61 | // threadId integer NOT NULL,
62 | // url varchar(255) NOT NULL,
63 | // start long NOT NULL DEFAULT(0),
64 | // end long NOT NULL,
65 | // progress long NOT NULL DEFAULT(0)
66 | // );
67 |
68 | }
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/db/DownloadDBController.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.db;
2 |
3 | import com.ixuea.android.downloader.domain.DownloadInfo;
4 | import com.ixuea.android.downloader.domain.DownloadThreadInfo;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
10 | */
11 |
12 | public interface DownloadDBController {
13 |
14 | List findAllDownloading();
15 |
16 | List findAllDownloaded();
17 |
18 | DownloadInfo findDownloadedInfoById(String id);
19 |
20 | void pauseAllDownloading();
21 |
22 | void createOrUpdate(DownloadInfo downloadInfo);
23 |
24 | void createOrUpdate(DownloadThreadInfo downloadThreadInfo);
25 |
26 | void delete(DownloadInfo downloadInfo);
27 |
28 | void delete(DownloadThreadInfo download);
29 | }
30 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/domain/DownloadInfo.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.domain;
2 |
3 | import android.text.TextUtils;
4 |
5 | import androidx.annotation.IntDef;
6 |
7 | import com.ixuea.android.downloader.callback.DownloadListener;
8 | import com.ixuea.android.downloader.exception.DownloadException;
9 |
10 | import java.io.Serializable;
11 | import java.lang.annotation.Retention;
12 | import java.lang.annotation.RetentionPolicy;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
17 | */
18 |
19 | public class DownloadInfo implements Serializable {
20 |
21 | /**
22 | *
23 | */
24 | public static final int STATUS_NONE = 0;
25 | /**
26 | *
27 | */
28 | public static final int STATUS_PREPARE_DOWNLOAD = 1;
29 | /**
30 | *
31 | */
32 | public static final int STATUS_DOWNLOADING = 2;
33 | /**
34 | *
35 | */
36 | public static final int STATUS_WAIT = 3;
37 | /**
38 | *
39 | */
40 | public static final int STATUS_PAUSED = 4;
41 | /**
42 | *
43 | */
44 | public static final int STATUS_COMPLETED = 5;
45 | /**
46 | *
47 | */
48 | public static final int STATUS_ERROR = 6;
49 | /**
50 | *
51 | */
52 | public static final int STATUS_REMOVED = 7;
53 |
54 |
55 | private transient DownloadListener downloadListener;
56 | private DownloadException exception;
57 |
58 | //--------------------The following fields require persistence.
59 | /**
60 | * Each download task id.
61 | */
62 | private String id;
63 | /**
64 | * Time to create a download task.
65 | */
66 | private long createAt;
67 | private String uri;
68 | private String path;
69 | private long size;
70 | private long progress;
71 | @DownloadStatus
72 | private int status;
73 | /**
74 | * Support multi-threaded download.
75 | */
76 | private int supportRanges;
77 |
78 | private List downloadThreadInfos;
79 |
80 |
81 | public DownloadException getException() {
82 | return exception;
83 | }
84 |
85 | public void setException(DownloadException exception) {
86 | this.exception = exception;
87 | }
88 |
89 | public long getCreateAt() {
90 | return createAt;
91 | }
92 |
93 | public void setCreateAt(long createAt) {
94 | this.createAt = createAt;
95 | }
96 |
97 | public String getUri() {
98 | return uri;
99 | }
100 |
101 | public void setUri(String uri) {
102 | this.uri = uri;
103 | }
104 |
105 | public String getPath() {
106 | return path;
107 | }
108 |
109 | public void setPath(String path) {
110 | this.path = path;
111 | }
112 |
113 | public long getSize() {
114 | return size;
115 | }
116 |
117 | public void setSize(long size) {
118 | this.size = size;
119 | }
120 |
121 | public long getProgress() {
122 | return progress;
123 | }
124 |
125 | public void setProgress(long progress) {
126 | this.progress = progress;
127 | }
128 |
129 | public DownloadListener getDownloadListener() {
130 | return downloadListener;
131 | }
132 |
133 | public void setDownloadListener(
134 | DownloadListener downloadListener) {
135 | this.downloadListener = downloadListener;
136 | }
137 |
138 | @DownloadStatus
139 | public int getStatus() {
140 | return status;
141 | }
142 |
143 | public void setStatus(int status) {
144 | this.status = status;
145 | }
146 |
147 | public String getId() {
148 | return id;
149 | }
150 |
151 | public void setId(String id) {
152 | this.id = id;
153 | }
154 |
155 | public int getSupportRanges() {
156 | return supportRanges;
157 | }
158 |
159 | public void setSupportRanges(int supportRanges) {
160 | this.supportRanges = supportRanges;
161 | }
162 |
163 | public boolean isSupportRanges() {
164 | return supportRanges == 0;
165 | }
166 |
167 | public void setSupportRanges(boolean supportRanges) {
168 | this.supportRanges = supportRanges ? 0 : 1;
169 | }
170 |
171 | public List getDownloadThreadInfos() {
172 | return downloadThreadInfos;
173 | }
174 |
175 | public void setDownloadThreadInfos(List downloadThreadInfos) {
176 | this.downloadThreadInfos = downloadThreadInfos;
177 | }
178 |
179 | @Override
180 | public boolean equals(Object o) {
181 | if (this == o) return true;
182 | if (o == null || getClass() != o.getClass()) return false;
183 |
184 | DownloadInfo that = (DownloadInfo) o;
185 |
186 | return id.equals(that.id);
187 | }
188 |
189 | @Override
190 | public int hashCode() {
191 | return id.hashCode();
192 | }
193 |
194 | public boolean isPause() {
195 | return status == DownloadInfo.STATUS_PAUSED || status == DownloadInfo.STATUS_ERROR
196 | || status == STATUS_REMOVED;
197 | }
198 |
199 | /**
200 | * Download info status.
201 | */
202 | @IntDef({STATUS_NONE, STATUS_PREPARE_DOWNLOAD, STATUS_DOWNLOADING, STATUS_WAIT, STATUS_PAUSED,
203 | STATUS_COMPLETED, STATUS_ERROR, STATUS_REMOVED})
204 | @Retention(RetentionPolicy.SOURCE)
205 | public @interface DownloadStatus {
206 |
207 | }
208 |
209 | /**
210 | * Download info builder.
211 | */
212 | public static final class Builder {
213 |
214 | private static final String DEFAULT_ENCODE = "utf-8";
215 |
216 | private String id;
217 | private long createAt = -1;
218 | private String url;
219 | private String path;
220 |
221 | public Builder() {
222 |
223 | }
224 |
225 |
226 | public Builder setCreateAt(long createAt) {
227 | this.createAt = createAt;
228 | return this;
229 | }
230 |
231 |
232 | public Builder setUrl(String url) {
233 | this.url = url;
234 | return this;
235 | }
236 |
237 |
238 | public Builder setPath(String path) {
239 | this.path = path;
240 | return this;
241 | }
242 |
243 | public Builder setId(String id) {
244 | this.id = id;
245 | return this;
246 | }
247 |
248 | public DownloadInfo build() {
249 | DownloadInfo downloadInfo = new DownloadInfo();
250 |
251 | if (TextUtils.isEmpty(url)) {
252 | throw new DownloadException(DownloadException.EXCEPTION_URL_NULL, "uri cannot be null.");
253 | }
254 |
255 | downloadInfo.setUri(url);
256 |
257 | if (TextUtils.isEmpty(path)) {
258 | throw new DownloadException(DownloadException.EXCEPTION_PATH_NULL, "path cannot be null.");
259 | }
260 |
261 | downloadInfo.setPath(path);
262 |
263 | if (createAt == -1) {
264 | createAt = System.currentTimeMillis();
265 | }
266 |
267 | downloadInfo.setCreateAt(createAt);
268 |
269 | if (TextUtils.isEmpty(id)) {
270 | downloadInfo.setId(url);
271 | } else {
272 | downloadInfo.setId(id);
273 | }
274 |
275 | return downloadInfo;
276 | }
277 |
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/domain/DownloadThreadInfo.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.domain;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
7 | */
8 |
9 | public class DownloadThreadInfo implements Serializable {
10 |
11 | /**
12 | * Each download thread id.
13 | */
14 | private int id;
15 | private int threadId;
16 | private String downloadInfoId;
17 | private String uri;
18 | private long start;
19 | private long end;
20 | private long progress;
21 |
22 | public DownloadThreadInfo(int threadId, String downloadInfoId, String uri, long start,
23 | long end) {
24 | this.id = downloadInfoId.hashCode() + threadId;
25 | this.threadId = threadId;
26 | this.downloadInfoId = downloadInfoId;
27 | this.uri = uri;
28 | this.start = start;
29 | this.end = end;
30 | }
31 |
32 | public DownloadThreadInfo() {
33 | }
34 |
35 | public int getId() {
36 | return id;
37 | }
38 |
39 | public void setId(int id) {
40 | this.id = id;
41 | }
42 |
43 | public String getDownloadInfoId() {
44 | return downloadInfoId;
45 | }
46 |
47 | public void setDownloadInfoId(String downloadInfoId) {
48 | this.downloadInfoId = downloadInfoId;
49 | }
50 |
51 | public int getThreadId() {
52 | return threadId;
53 | }
54 |
55 | public void setThreadId(int threadId) {
56 | this.threadId = threadId;
57 | }
58 |
59 | public String getUri() {
60 | return uri;
61 | }
62 |
63 | public void setUri(String uri) {
64 | this.uri = uri;
65 | }
66 |
67 | public long getStart() {
68 | return start;
69 | }
70 |
71 | public void setStart(long start) {
72 | this.start = start;
73 | }
74 |
75 | public long getEnd() {
76 | return end;
77 | }
78 |
79 | public void setEnd(long end) {
80 | this.end = end;
81 | }
82 |
83 | public long getProgress() {
84 | return progress;
85 | }
86 |
87 | public void setProgress(long progress) {
88 | this.progress = progress;
89 | }
90 |
91 | public boolean isThreadDownloadSuccess() {
92 | return progress >= (end - start);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/exception/DownloadException.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.exception;
2 |
3 | import androidx.annotation.IntDef;
4 |
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 |
8 | /**
9 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
10 | */
11 |
12 | public class DownloadException extends RuntimeException {
13 |
14 | /**
15 | *
16 | */
17 | public static final int EXCEPTION_URL_NULL = 0;
18 | /**
19 | *
20 | */
21 | public static final int EXCEPTION_PATH_NULL = 1;
22 | /**
23 | *
24 | */
25 | public static final int EXCEPTION_URL_ERROR = 2;
26 | /**
27 | *
28 | */
29 | public static final int EXCEPTION_SERVER_ERROR = 3;
30 | /**
31 | *
32 | */
33 | public static final int EXCEPTION_PROTOCOL = 4;
34 | /**
35 | *
36 | */
37 | public static final int EXCEPTION_IO_EXCEPTION = 5;
38 | /**
39 | *
40 | */
41 | public static final int EXCEPTION_FILE_SIZE_ZERO = 6;
42 | /**
43 | *
44 | */
45 | public static final int EXCEPTION_PAUSE = 7;
46 | /**
47 | *
48 | */
49 | public static final int EXCEPTION_SERVER_SUPPORT_CODE = 8;
50 |
51 | /**
52 | *
53 | */
54 | public static final int EXCEPTION_OTHER = 9;
55 |
56 | private int code;
57 |
58 | public DownloadException(@ExceptionType int code) {
59 | this.code = code;
60 | }
61 |
62 | public DownloadException(@ExceptionType int code, String message) {
63 | super(message);
64 | this.code = code;
65 | }
66 |
67 | public DownloadException(@ExceptionType int code, String message, Throwable cause) {
68 | super(message, cause);
69 | this.code = code;
70 | }
71 |
72 | public DownloadException(@ExceptionType int code, Throwable cause) {
73 | super(cause);
74 | this.code = code;
75 | }
76 |
77 |
78 | public int getCode() {
79 | return code;
80 | }
81 |
82 | public void setCode(int code) {
83 | this.code = code;
84 | }
85 |
86 | /**
87 | * Download exception type.
88 | */
89 | @IntDef({EXCEPTION_URL_NULL, EXCEPTION_PATH_NULL, EXCEPTION_URL_ERROR, EXCEPTION_SERVER_ERROR,
90 | EXCEPTION_PROTOCOL, EXCEPTION_IO_EXCEPTION, EXCEPTION_FILE_SIZE_ZERO, EXCEPTION_PAUSE,
91 | EXCEPTION_SERVER_SUPPORT_CODE, EXCEPTION_OTHER})
92 | @Retention(RetentionPolicy.SOURCE)
93 | public @interface ExceptionType {
94 |
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/downloader/src/main/java/com/ixuea/android/downloader/exception/DownloadPauseException.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader.exception;
2 |
3 | /**
4 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021.
5 | */
6 |
7 | public class DownloadPauseException extends DownloadException {
8 |
9 |
10 | public DownloadPauseException(@ExceptionType int code) {
11 | super(code);
12 | }
13 |
14 | public DownloadPauseException(@ExceptionType int code, String message) {
15 | super(code, message);
16 | }
17 |
18 | public DownloadPauseException(@ExceptionType int code, String message, Throwable cause) {
19 | super(code, message, cause);
20 | }
21 |
22 | public DownloadPauseException(@ExceptionType int code, Throwable cause) {
23 | super(code, cause);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/downloader/src/test/java/com/ixuea/android/downloader/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ixuea.android.downloader;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Sep 18 13:34:26 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | jcenter() // Warning: this repository is going to shut down soon
7 | }
8 | }
9 | rootProject.name = "android-downloader"
10 | include ':app'
11 | include ':downloader'
12 |
--------------------------------------------------------------------------------