├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml └── misc.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── art │ ├── downloadAFile.png │ ├── downloaded.png │ ├── downloading.png │ ├── home.png │ └── useInList.png ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ixuea │ │ └── android │ │ └── downloader │ │ └── simple │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ixuea │ │ │ └── android │ │ │ ├── common │ │ │ ├── activity │ │ │ │ └── BaseActivity.java │ │ │ ├── adapter │ │ │ │ ├── BaseRecyclerViewAdapter.java │ │ │ │ └── CustomFragmentPagerAdapter.java │ │ │ └── fragment │ │ │ │ └── BaseFragment.java │ │ │ └── downloader │ │ │ └── simple │ │ │ ├── MainActivity.java │ │ │ ├── activity │ │ │ ├── DownloadDetailActivity.java │ │ │ ├── DownloadManagerActivity.java │ │ │ ├── ListActivity.java │ │ │ └── SimpleActivity.java │ │ │ ├── adapter │ │ │ ├── DownloadAdapter.java │ │ │ ├── DownloadListAdapter.java │ │ │ └── DownloadManagerAdapter.java │ │ │ ├── callback │ │ │ └── MyDownloadListener.java │ │ │ ├── db │ │ │ ├── DBController.java │ │ │ └── DBHelper.java │ │ │ ├── domain │ │ │ ├── MyBusinessInfLocal.java │ │ │ ├── MyBusinessInfo.java │ │ │ ├── MyDownloadInfLocal.java │ │ │ └── MyDownloadThreadInfoLocal.java │ │ │ ├── event │ │ │ └── DownloadStatusChanged.java │ │ │ ├── fragment │ │ │ ├── DownloadedFragment.java │ │ │ └── DownloadingFragment.java │ │ │ └── util │ │ │ └── FileUtil.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_download_detail.xml │ │ ├── activity_download_manager.xml │ │ ├── activity_list.xml │ │ ├── activity_main.xml │ │ ├── activity_simple.xml │ │ ├── fragment_downloaded.xml │ │ ├── fragment_downloading.xml │ │ └── item_download_info.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── ixuea │ └── android │ └── downloader │ └── simple │ └── ExampleUnitTest.java ├── build.gradle ├── docs └── zh.md ├── downloader ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ixuea │ │ └── android │ │ └── downloader │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── ixuea │ │ └── android │ │ └── downloader │ │ ├── DownloadManagerImpl.java │ │ ├── DownloadService.java │ │ ├── callback │ │ ├── AbsDownloadListener.java │ │ ├── DownloadListener.java │ │ └── DownloadManager.java │ │ ├── config │ │ └── Config.java │ │ ├── core │ │ ├── DownloadResponse.java │ │ ├── DownloadResponseImpl.java │ │ ├── DownloadTaskImpl.java │ │ ├── task │ │ │ ├── DownloadTask.java │ │ │ └── GetFileInfoTask.java │ │ └── thread │ │ │ └── DownloadThread.java │ │ ├── db │ │ ├── DefaultDownloadDBController.java │ │ ├── DefaultDownloadHelper.java │ │ └── DownloadDBController.java │ │ ├── domain │ │ ├── DownloadInfo.java │ │ └── DownloadThreadInfo.java │ │ └── exception │ │ ├── DownloadException.java │ │ └── DownloadPauseException.java │ └── test │ └── java │ └── com │ └── ixuea │ └── android │ └── downloader │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #Android project default 2 | *.iml 3 | .gradle 4 | /local.properties 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | 18 | # Built application files 19 | *.apk 20 | output.json 21 | *.ap_ 22 | *.aab 23 | 24 | # Files for the ART/Dalvik VM 25 | *.dex 26 | 27 | # Java class files 28 | *.class 29 | 30 | # Generated files 31 | bin/ 32 | gen/ 33 | out/ 34 | # Uncomment the following line in case you need and you don't have the release build type files in your app 35 | # release/ 36 | 37 | # Gradle files 38 | .gradle/ 39 | build/ 40 | 41 | # Local configuration file (sdk path, etc) 42 | local.properties 43 | 44 | # Proguard folder generated by Eclipse 45 | proguard/ 46 | 47 | # Log Files 48 | *.log 49 | 50 | # Android Studio Navigation editor temp files 51 | .navigation/ 52 | 53 | # Android Studio captures folder 54 | captures/ 55 | 56 | #Android other 57 | .idea/deploymentTargetDropDown.xml 58 | 59 | # IntelliJ 60 | *.iml 61 | .idea/workspace.xml 62 | .idea/tasks.xml 63 | .idea/gradle.xml 64 | .idea/assetWizardSettings.xml 65 | .idea/dictionaries 66 | .idea/libraries 67 | # Android Studio 3 in .gitignore file. 68 | .idea/caches 69 | .idea/modules.xml 70 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 71 | .idea/navEditor.xml 72 | 73 | # Keystore files 74 | # Uncomment the following lines if you do not want to check your keystore files in. 75 | #*.jks 76 | #*.keystore 77 | 78 | # External native build folder generated in Android Studio 2.2 and later 79 | .externalNativeBuild 80 | 81 | # Google Services (e.g. APIs or Firebase) 82 | # google-services.json 83 | 84 | # Freeline 85 | freeline.py 86 | freeline/ 87 | freeline_project_description.json 88 | 89 | # fastlane 90 | fastlane/report.xml 91 | fastlane/Preview.html 92 | fastlane/screenshots 93 | fastlane/test_output 94 | fastlane/readme.md 95 | 96 | # Version control 97 | vcs.xml 98 | 99 | # lint 100 | lint/intermediates/ 101 | lint/generated/ 102 | lint/outputs/ 103 | lint/tmp/ 104 | # lint/reports/ 105 | 106 | # macOS finder 107 | .DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-downloader 2 | ===== 3 | 4 | English | [中文][14] 5 | 6 | [![](https://jitpack.io/v/com.ixuea/android-downloader.svg)](https://jitpack.io/#com.ixuea/android-downloader) 7 | ![License](https://img.shields.io/github/license/ixuea/android-downloader) 8 | 9 | [Report an issue][10], iOS and macOS use [CocoaDownloader][12]. 10 | 11 | Android Downloader is a open source multithread and mulitask downloadInfo framework for Android. 12 | 13 | Try out the sample application [on the Apk file][20]. 14 | 15 | 16 | 17 | 18 | 19 | 20 | Download 21 | ======= 22 | 23 | Add it in your root build.gradle at the end of repositories: 24 | 25 | ``` 26 | allprojects { 27 | repositories { 28 | ... 29 | maven { url 'https://jitpack.io' } 30 | } 31 | } 32 | ``` 33 | 34 | Add the dependency: 35 | 36 | ```gradle 37 | dependencies { 38 | implementation 'com.ixuea:android-downloader:latest' 39 | } 40 | ``` 41 | 42 | For info on using the bleeding edge, see the [Snapshots][50] wiki page. 43 | 44 | ProGuard 45 | ======= 46 | 47 | If your project uses ProGuard, you need to add the following configuration to your project proguard-rules.pro file 48 | 49 | ```pro 50 | -keep public class * implements com.ixuea.android.downloader.db.DownloadDBController 51 | ``` 52 | 53 | How do I use Android Downloader? 54 | ======= 55 | 56 | For more information on [GitHub wiki][200] and [Javadocs][201]. 57 | 58 | 1.Add network network permissions() 59 | ------- 60 | 61 | ```xml 62 | 63 | ``` 64 | 65 | 2.Create a DownloadManager instance 66 | ----------------------------------- 67 | 68 | ```java 69 | downloadManager = DownloadService.getDownloadManager(context.getApplicationContext()); 70 | ``` 71 | 72 | Simple use as follows 73 | 74 | 3.Download a file 75 | ----------------- 76 | 77 | ```java 78 | //create download info set download uri and save path. 79 | File targetFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "a.apk"); 80 | final DownloadInfo downloadInfo = new DownloadInfo.Builder().setUrl("http://example.com/a.apk") 81 | .setPath(targetFile.getAbsolutePath()) 82 | .build(); 83 | 84 | //set download callback. 85 | downloadInfo.setDownloadListener(new DownloadListener() { 86 | 87 | @Override 88 | public void onStart() { 89 | tv_download_info.setText("Prepare downloading"); 90 | } 91 | 92 | @Override 93 | public void onWaited() { 94 | tv_download_info.setText("Waiting"); 95 | bt_download_button.setText("Pause"); 96 | } 97 | 98 | @Override 99 | public void onPaused() { 100 | bt_download_button.setText("Continue"); 101 | tv_download_info.setText("Paused"); 102 | } 103 | 104 | @Override 105 | public void onDownloading(long progress, long size) { 106 | tv_download_info 107 | .setText(FileUtil.formatFileSize(progress) + "/" + FileUtil 108 | .formatFileSize(size)); 109 | bt_download_button.setText("Pause"); 110 | } 111 | 112 | @Override 113 | public void onRemoved() { 114 | bt_download_button.setText("Download"); 115 | tv_download_info.setText(""); 116 | downloadInfo = null; 117 | } 118 | 119 | @Override 120 | public void onDownloadSuccess() { 121 | bt_download_button.setText("Delete"); 122 | tv_download_info.setText("Download success"); 123 | } 124 | 125 | @Override 126 | public void onDownloadFailed(DownloadException e) { 127 | bt_download_button.setText("Continue"); 128 | tv_download_info.setText("Download fail:" + e.getMessage()); 129 | } 130 | }); 131 | 132 | //submit download info to download manager. 133 | downloadManager.download(downloadInfo); 134 | ``` 135 | 136 | Compatibility 137 | ======= 138 | 139 | * **Android SDK**: Android Downloader requires a minimum API level of 21. 140 | 141 | 142 | Build 143 | ======= 144 | 145 | 146 | Samples 147 | ======= 148 | 149 | Follow the steps in the [Build][60] section to setup the project and then: 150 | 151 | ```gradle 152 | ./gradlew :app:run 153 | ``` 154 | 155 | You may also find precompiled APKs on the releases page. 156 | 157 | ## More 158 | 159 | See the example code. 160 | 161 | ## Author 162 | 163 | Smile - @ixueadev on GitHub, Email is ixueadev@163.com, See more ixuea([http://www.ixuea.com][100]) 164 | 165 | Android development QQ group: 702321063. 166 | 167 | [10]: https://github.com/ixuea/android-downloader/issues/new 168 | [12]: http://a.ixuea.com/8 169 | [13]: https://github.com/ixuea/android-downloader 170 | [14]: https://github.com/ixuea/android-downloader/blob/master/docs/zh.md 171 | [20]: https://github.com/ixuea/android-downloader/releases 172 | 173 | [30]: https://raw.github.com/ixuea/android-downloader/master/samples/art/download-a-file.png 174 | [31]: https://raw.github.com/ixuea/android-downloader/master/samples/art/use-in-list.png 175 | [32]: https://raw.github.com/ixuea/android-downloader/master/samples/art/download-manager-downloading.png 176 | [33]: https://raw.github.com/ixuea/android-downloader/master/samples/art/download-manager-downloaded.png 177 | 178 | [40]: https://github.com/ixuea/android-downloader/releases 179 | [50]: https://github.com/ixuea/android-downloader/releases 180 | [60]: https://github.com/ixuea/android-downloader#build 181 | 182 | [100]: http://a.ixuea.com/3 183 | 184 | [200]: https://github.com/ixuea/android-downloader/wiki 185 | [201]: https://github.com/ixuea/android-downloader/wiki 186 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/art/downloadAFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/app/art/downloadAFile.png -------------------------------------------------------------------------------- /app/art/downloaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/app/art/downloaded.png -------------------------------------------------------------------------------- /app/art/downloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/app/art/downloading.png -------------------------------------------------------------------------------- /app/art/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/app/art/home.png -------------------------------------------------------------------------------- /app/art/useInList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixuea/android-downloader/d3e8bfa261d86c2954ebb6cdf4d985c4d9eb4a47/app/art/useInList.png -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk rootProject.ext.compileSdk 7 | 8 | defaultConfig { 9 | applicationId "com.ixuea.android.downloader.simple" 10 | minSdk rootProject.ext.minSdk 11 | targetSdk rootProject.ext.targetSdk 12 | versionCode 300 13 | versionName "3.0.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | 29 | lintOptions { 30 | abortOnError false 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation 'androidx.appcompat:appcompat:1.3.1' 37 | implementation 'com.google.android.material:material:1.4.0' 38 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0' 39 | 40 | implementation project(':downloader') 41 | 42 | //image load 43 | implementation 'com.github.bumptech.glide:glide:4.12.0' 44 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' 45 | 46 | implementation 'org.greenrobot:eventbus:3.2.0' 47 | 48 | //ormlite 49 | implementation 'com.j256.ormlite:ormlite-android:5.5' 50 | implementation 'com.j256.ormlite:ormlite-core:5.5' 51 | 52 | testImplementation 'junit:junit:4.+' 53 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 54 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 55 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ixuea/android/downloader/simple/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.ixuea.android.downloader.simple", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/common/activity/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.common.activity; 2 | 3 | import android.content.Intent; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import androidx.fragment.app.FragmentActivity; 9 | 10 | /** 11 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 12 | */ 13 | public class BaseActivity extends AppCompatActivity { 14 | 15 | /** 16 | * Find all view. 17 | */ 18 | protected void initView() { 19 | } 20 | 21 | /** 22 | * Set some style. 23 | */ 24 | protected void initStyle() { 25 | } 26 | 27 | /** 28 | * Set data. 29 | */ 30 | protected void initData() { 31 | 32 | } 33 | 34 | /** 35 | * Bind view listener. 36 | */ 37 | protected void initListener() { 38 | } 39 | 40 | protected void init() { 41 | beforeInit(); 42 | initView(); 43 | initStyle(); 44 | initData(); 45 | initListener(); 46 | } 47 | 48 | protected void beforeInit() { 49 | 50 | } 51 | 52 | @Override 53 | public void setContentView(int layoutResID) { 54 | super.setContentView(layoutResID); 55 | init(); 56 | } 57 | 58 | @Override 59 | public void setContentView(View view) { 60 | super.setContentView(view); 61 | init(); 62 | } 63 | 64 | @Override 65 | public void setContentView(View view, ViewGroup.LayoutParams params) { 66 | super.setContentView(view, params); 67 | init(); 68 | } 69 | 70 | 71 | protected FragmentActivity getActivity() { 72 | return this; 73 | } 74 | 75 | 76 | public void toActivity(Class clazz) { 77 | toActivity(new Intent(getActivity(), clazz)); 78 | } 79 | 80 | public void toActivity(Intent intent) { 81 | startActivity(intent); 82 | } 83 | 84 | public void toActivityAfterFinishThis(Class clazz) { 85 | toActivity(clazz); 86 | finish(); 87 | } 88 | 89 | public void toActivityAfterFinishThis(Intent intent) { 90 | toActivity(intent); 91 | finish(); 92 | } 93 | 94 | public void toActivityForResult(Intent intent, int requestCode) { 95 | startActivityForResult(intent, requestCode); 96 | } 97 | 98 | 99 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/common/adapter/BaseRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.common.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.recyclerview.widget.RecyclerView; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 13 | * 14 | * @param data type 15 | * @param ViewHolder type 16 | */ 17 | public abstract class BaseRecyclerViewAdapter extends 18 | RecyclerView.Adapter { 19 | 20 | protected final Context context; 21 | protected OnItemClickListener onItemClickListener; 22 | private List data = new ArrayList<>(); 23 | 24 | public BaseRecyclerViewAdapter(Context context) { 25 | this.context = context; 26 | } 27 | 28 | @Override 29 | public int getItemCount() { 30 | return data.size(); 31 | } 32 | 33 | public D getData(int position) { 34 | return data.get(position); 35 | } 36 | 37 | public void appendData(List data) { 38 | this.data.addAll(data); 39 | notifyDataSetChanged(); 40 | } 41 | 42 | public void clearData() { 43 | this.data.clear(); 44 | notifyDataSetChanged(); 45 | } 46 | 47 | /** 48 | * @param onItemClickListener 49 | */ 50 | public void setOnItemClickListener( 51 | OnItemClickListener onItemClickListener) { 52 | this.onItemClickListener = onItemClickListener; 53 | } 54 | 55 | public List getData() { 56 | return data; 57 | } 58 | 59 | public void setData(List data) { 60 | this.data.clear(); 61 | this.data.addAll(data); 62 | notifyDataSetChanged(); 63 | } 64 | 65 | /** 66 | * Item click listener. 67 | */ 68 | public interface OnItemClickListener { 69 | 70 | void onItemClick(int position); 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/common/adapter/CustomFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.common.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.fragment.app.FragmentManager; 6 | import androidx.fragment.app.FragmentPagerAdapter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 14 | * 15 | * @param item data type. 16 | */ 17 | public abstract class CustomFragmentPagerAdapter extends FragmentPagerAdapter { 18 | 19 | protected final List datas = new ArrayList(); 20 | protected final Context context; 21 | protected T data; 22 | 23 | public CustomFragmentPagerAdapter(FragmentManager fm, Context context) { 24 | super(fm); 25 | this.context = context; 26 | } 27 | 28 | @Override 29 | public int getCount() { 30 | return datas.size(); 31 | } 32 | 33 | public T getData(int position) { 34 | return datas.get(position); 35 | } 36 | 37 | public void setData(List data) { 38 | if (data != null && data.size() > 0) { 39 | datas.clear(); 40 | datas.addAll(data); 41 | notifyDataSetChanged(); 42 | } 43 | } 44 | 45 | public void clear() { 46 | datas.clear(); 47 | } 48 | 49 | public void clearAndNotify() { 50 | datas.clear(); 51 | notifyDataSetChanged(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/common/fragment/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.common.fragment; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.fragment.app.Fragment; 11 | 12 | /** 13 | * Public fragment parent class,all the classes should be inherited. 14 | */ 15 | public abstract class BaseFragment extends Fragment { 16 | 17 | 18 | @Override 19 | public void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | beforeInit(); 22 | } 23 | 24 | protected void beforeInit() { 25 | } 26 | 27 | @Nullable 28 | @Override 29 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 30 | @Nullable Bundle savedInstanceState) { 31 | View view = getLayoutView(inflater, container, savedInstanceState); 32 | if (isAutoBind()) { 33 | bindView(view); 34 | } 35 | return view; 36 | } 37 | 38 | 39 | protected void bindView(View view) { 40 | } 41 | 42 | 43 | protected boolean isAutoBind() { 44 | return true; 45 | } 46 | 47 | 48 | protected abstract View getLayoutView(LayoutInflater inflater, ViewGroup container, 49 | Bundle savedInstanceState); 50 | 51 | @Override 52 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 53 | initView(); 54 | initStyle(); 55 | initData(); 56 | initListener(); 57 | super.onViewCreated(view, savedInstanceState); 58 | } 59 | 60 | 61 | protected void initView() { 62 | 63 | } 64 | 65 | 66 | protected void initStyle() { 67 | } 68 | 69 | 70 | protected void initData() { 71 | 72 | } 73 | 74 | 75 | protected void initListener() { 76 | } 77 | 78 | public void toActivity(Class clazz) { 79 | toActivity(new Intent(getActivity(), clazz)); 80 | } 81 | 82 | public void toActivity(Intent intent) { 83 | startActivity(intent); 84 | } 85 | 86 | public void toActivityAfterFinishThis(Class clazz) { 87 | toActivity(clazz); 88 | getActivity().finish(); 89 | } 90 | 91 | public void toActivityAfterFinishThis(Intent intent) { 92 | toActivity(intent); 93 | getActivity().finish(); 94 | } 95 | 96 | public void toActivityForResult(Intent intent, int requestCode) { 97 | startActivityForResult(intent, requestCode); 98 | } 99 | 100 | 101 | @Override 102 | public void onDestroyView() { 103 | super.onDestroyView(); 104 | // ButterKnife.unbind(this); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | 9 | import com.ixuea.android.downloader.simple.activity.DownloadManagerActivity; 10 | import com.ixuea.android.downloader.simple.activity.ListActivity; 11 | import com.ixuea.android.downloader.simple.activity.SimpleActivity; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | } 20 | 21 | public void downloadAFile(View view) { 22 | startActivity(new Intent(this, SimpleActivity.class)); 23 | } 24 | 25 | public void useInList(View view) { 26 | startActivity(new Intent(this, ListActivity.class)); 27 | } 28 | 29 | public void downloadManager(View view) { 30 | startActivity(new Intent(this, DownloadManagerActivity.class)); 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/activity/DownloadDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.activity; 2 | 3 | import android.os.Bundle; 4 | import android.os.Environment; 5 | import android.view.View; 6 | import android.view.View.OnClickListener; 7 | import android.widget.Button; 8 | import android.widget.ImageView; 9 | import android.widget.ProgressBar; 10 | import android.widget.TextView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.ixuea.android.common.activity.BaseActivity; 14 | import com.ixuea.android.downloader.DownloadService; 15 | import com.ixuea.android.downloader.callback.DownloadManager; 16 | import com.ixuea.android.downloader.domain.DownloadInfo; 17 | import com.ixuea.android.downloader.domain.DownloadInfo.Builder; 18 | import com.ixuea.android.downloader.simple.R; 19 | import com.ixuea.android.downloader.simple.callback.MyDownloadListener; 20 | import com.ixuea.android.downloader.simple.domain.MyBusinessInfo; 21 | import com.ixuea.android.downloader.simple.util.FileUtil; 22 | 23 | import java.io.File; 24 | import java.lang.ref.SoftReference; 25 | 26 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_COMPLETED; 27 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_REMOVED; 28 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_WAIT; 29 | 30 | /** 31 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 32 | */ 33 | public class DownloadDetailActivity extends BaseActivity { 34 | 35 | public static final String DATA = "DATA"; 36 | private DownloadManager downloadManager; 37 | private DownloadInfo downloadInfo; 38 | 39 | private ImageView iv_icon; 40 | private TextView tv_size; 41 | private TextView tv_status; 42 | private ProgressBar pb; 43 | private TextView tv_name; 44 | private Button bt_action; 45 | private MyBusinessInfo data; 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_download_detail); 51 | } 52 | 53 | 54 | @Override 55 | protected void initView() { 56 | super.initView(); 57 | iv_icon = (ImageView) findViewById(R.id.iv_icon); 58 | tv_size = (TextView) findViewById(R.id.tv_size); 59 | tv_status = (TextView) findViewById(R.id.tv_status); 60 | pb = (ProgressBar) findViewById(R.id.pb); 61 | tv_name = (TextView) findViewById(R.id.tv_name); 62 | bt_action = (Button) findViewById(R.id.bt_action); 63 | } 64 | 65 | @SuppressWarnings("unchecked") 66 | @Override 67 | protected void initData() { 68 | super.initData(); 69 | data = (MyBusinessInfo) getIntent().getSerializableExtra(DATA); 70 | Glide.with(this).load(data.getIcon()).into(iv_icon); 71 | 72 | downloadManager = DownloadService.getDownloadManager(getApplicationContext()); 73 | 74 | downloadInfo = downloadManager.getDownloadById(data.getUrl()); 75 | 76 | if (downloadInfo != null) { 77 | downloadInfo 78 | .setDownloadListener(new MyDownloadListener(new SoftReference(null)) { 79 | 80 | @Override 81 | public void onRefresh() { 82 | refresh(); 83 | } 84 | }); 85 | } 86 | 87 | refresh(); 88 | 89 | bt_action.setOnClickListener(new OnClickListener() { 90 | @Override 91 | public void onClick(View v) { 92 | if (downloadInfo != null) { 93 | 94 | switch (downloadInfo.getStatus()) { 95 | case DownloadInfo.STATUS_NONE: 96 | case DownloadInfo.STATUS_PAUSED: 97 | case DownloadInfo.STATUS_ERROR: 98 | 99 | //resume downloadInfo 100 | downloadManager.resume(downloadInfo); 101 | break; 102 | 103 | case DownloadInfo.STATUS_DOWNLOADING: 104 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 105 | case STATUS_WAIT: 106 | //pause downloadInfo 107 | downloadManager.pause(downloadInfo); 108 | break; 109 | case DownloadInfo.STATUS_COMPLETED: 110 | downloadManager.remove(downloadInfo); 111 | break; 112 | } 113 | } else { 114 | // Create new download task 115 | File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d"); 116 | if (!d.exists()) { 117 | d.mkdirs(); 118 | } 119 | String path = d.getAbsolutePath().concat("/").concat(data.getName()); 120 | downloadInfo = new Builder().setUrl(data.getUrl()) 121 | .setPath(path) 122 | .build(); 123 | downloadInfo 124 | .setDownloadListener(new MyDownloadListener(new SoftReference(null)) { 125 | 126 | @Override 127 | public void onRefresh() { 128 | refresh(); 129 | } 130 | }); 131 | downloadManager.download(downloadInfo); 132 | } 133 | } 134 | }); 135 | } 136 | 137 | private void refresh() { 138 | if (downloadInfo == null) { 139 | tv_size.setText(""); 140 | pb.setProgress(0); 141 | bt_action.setText("Download"); 142 | tv_status.setText("not downloadInfo"); 143 | } else { 144 | switch (downloadInfo.getStatus()) { 145 | case DownloadInfo.STATUS_NONE: 146 | bt_action.setText("Download"); 147 | tv_status.setText("not downloadInfo"); 148 | break; 149 | case DownloadInfo.STATUS_PAUSED: 150 | case DownloadInfo.STATUS_ERROR: 151 | bt_action.setText("Continue"); 152 | tv_status.setText("paused"); 153 | try { 154 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 155 | } catch (Exception e) { 156 | e.printStackTrace(); 157 | } 158 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 159 | .formatFileSize(downloadInfo.getSize())); 160 | break; 161 | 162 | case DownloadInfo.STATUS_DOWNLOADING: 163 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 164 | bt_action.setText("Pause"); 165 | try { 166 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | } 170 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 171 | .formatFileSize(downloadInfo.getSize())); 172 | tv_status.setText("downloading"); 173 | break; 174 | case STATUS_COMPLETED: 175 | bt_action.setText("Delete"); 176 | try { 177 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 178 | } catch (Exception e) { 179 | e.printStackTrace(); 180 | } 181 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 182 | .formatFileSize(downloadInfo.getSize())); 183 | tv_status.setText("success"); 184 | break; 185 | case STATUS_REMOVED: 186 | tv_size.setText(""); 187 | pb.setProgress(0); 188 | bt_action.setText("Download"); 189 | tv_status.setText("not downloadInfo"); 190 | case STATUS_WAIT: 191 | tv_size.setText(""); 192 | pb.setProgress(0); 193 | bt_action.setText("Pause"); 194 | tv_status.setText("Waiting"); 195 | break; 196 | } 197 | 198 | } 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/activity/DownloadManagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.viewpager.widget.ViewPager; 6 | 7 | import com.google.android.material.tabs.TabLayout; 8 | import com.ixuea.android.common.activity.BaseActivity; 9 | import com.ixuea.android.downloader.simple.R; 10 | import com.ixuea.android.downloader.simple.adapter.DownloadManagerAdapter; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Download manager page. 17 | */ 18 | public class DownloadManagerActivity extends BaseActivity { 19 | 20 | private TabLayout tl; 21 | private ViewPager vp; 22 | private DownloadManagerAdapter downloadManagerAdapter; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_download_manager); 28 | } 29 | 30 | @Override 31 | protected void initView() { 32 | super.initView(); 33 | tl = (TabLayout) findViewById(R.id.tl); 34 | vp = (ViewPager) findViewById(R.id.vp); 35 | } 36 | 37 | @Override 38 | protected void initData() { 39 | super.initData(); 40 | 41 | downloadManagerAdapter = new DownloadManagerAdapter( 42 | getSupportFragmentManager(), getActivity()); 43 | 44 | List strings = new ArrayList<>(); 45 | strings.add("Downloading"); 46 | strings.add("Downloaded"); 47 | 48 | downloadManagerAdapter.setData(strings); 49 | vp.setAdapter(downloadManagerAdapter); 50 | tl.setupWithViewPager(vp); 51 | tl.setTabsFromPagerAdapter(downloadManagerAdapter); 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/activity/ListActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import androidx.recyclerview.widget.LinearLayoutManager; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import com.ixuea.android.common.activity.BaseActivity; 10 | import com.ixuea.android.common.adapter.BaseRecyclerViewAdapter.OnItemClickListener; 11 | import com.ixuea.android.downloader.simple.R; 12 | import com.ixuea.android.downloader.simple.adapter.DownloadListAdapter; 13 | import com.ixuea.android.downloader.simple.domain.MyBusinessInfo; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * How to use Android Downloader in RecyclerView. 20 | */ 21 | public class ListActivity extends BaseActivity implements OnItemClickListener { 22 | 23 | private static final int REQUEST_DOWNLOAD_DETAIL_PAGE = 100; 24 | 25 | private RecyclerView rv; 26 | private DownloadListAdapter downloadListAdapter; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_list); 32 | 33 | 34 | } 35 | 36 | @Override 37 | public void initListener() { 38 | downloadListAdapter.setOnItemClickListener(this); 39 | } 40 | 41 | @Override 42 | public void initData() { 43 | downloadListAdapter = new DownloadListAdapter(this); 44 | 45 | rv.setLayoutManager(new LinearLayoutManager(this)); 46 | rv.setAdapter(downloadListAdapter); 47 | 48 | downloadListAdapter.setData(getDownloadListData()); 49 | } 50 | 51 | private List getDownloadListData() { 52 | ArrayList myBusinessInfos = new ArrayList<>(); 53 | myBusinessInfos.add(new MyBusinessInfo("QQ", 54 | "https://pp.myapp.com/ma_icon/0/icon_6633_1631677952/96", 55 | "https://3b8637d9f6c334dab555e2afbdc16687.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/49F7A4E3B47E5828D02B2A10C580DB65.apk")); 56 | myBusinessInfos.add(new MyBusinessInfo("微信", 57 | "http://pp.myapp.com/ma_icon/0/icon_10910_1631077605/96", 58 | "https://21efcbaa5aca2783174d5e61409a56a4.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/DDF95F1C8F3421C3FD3D54AB4131284B.apk")); 59 | myBusinessInfos.add(new MyBusinessInfo("MOMO陌陌", 60 | "https://pp.myapp.com/ma_icon/0/icon_11381_1631702855/96", 61 | "https://6dbc06d1fdb4b9a104ca2a1f329d9ddc.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/E516E96AAC49419C7228983ED385A9D1.apk")); 62 | myBusinessInfos.add(new MyBusinessInfo("美颜相机", 63 | "https://pp.myapp.com/ma_icon/0/icon_1176832_1631606471/96", 64 | "https://d05e4fda19affc5d56fcc4706a8ae753.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/76826B5F2089F8A817E178947A354E1D.apk")); 65 | myBusinessInfos.add(new MyBusinessInfo("Chrome", 66 | "https://pp.myapp.com/ma_icon/0/icon_74260_1609923779/96", 67 | "https://637c54c96c2bd836f01af683939267ac.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/177FCB82643B8801AE50B9C2266C320C.apk")); 68 | myBusinessInfos.add(new MyBusinessInfo("淘宝", 69 | "https://pp.myapp.com/ma_icon/0/icon_5080_1630321832/96", 70 | "https://imtt.dd.qq.com/16891/apk/AE156F3160FDFD7AF900F3D2DCF0581D.apk")); 71 | myBusinessInfos.add(new MyBusinessInfo("手机天猫", 72 | "https://pp.myapp.com/ma_icon/0/icon_208787_1630818334/96", 73 | "https://imtt.dd.qq.com/16891/apk/8390758CC8FF6371B78E22C953EDE59E.apk")); 74 | myBusinessInfos.add(new MyBusinessInfo("支付宝", 75 | "http://pp.myapp.com/ma_icon/0/icon_5294_1631934458/96", 76 | "https://e50d9cb5335f94b56f730c9778f0af9e.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/94D8AB3EFA5C7FD345ABFCED1B9B90E0.apk")); 77 | myBusinessInfos.add(new MyBusinessInfo("和平精英-大文件", 78 | "https://pp.myapp.com/ma_icon/0/icon_52575843_1631064949/96", 79 | "https://a2b7a617e94763717ccf697f697eccf4.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/DAE19B232D28B8EC63045162C1CE22F5.apk")); 80 | return myBusinessInfos; 81 | } 82 | 83 | @Override 84 | public void initView() { 85 | rv = findViewById(R.id.rv); 86 | } 87 | 88 | @Override 89 | public void onItemClick(int position) { 90 | MyBusinessInfo data = downloadListAdapter.getData(position); 91 | Intent intent = new Intent(this, DownloadDetailActivity.class); 92 | intent.putExtra(DownloadDetailActivity.DATA, data); 93 | startActivityForResult(intent, REQUEST_DOWNLOAD_DETAIL_PAGE); 94 | } 95 | 96 | @Override 97 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 98 | super.onActivityResult(requestCode, resultCode, data); 99 | downloadListAdapter.notifyDataSetChanged(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/activity/SimpleActivity.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.activity; 2 | 3 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_COMPLETED; 4 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_REMOVED; 5 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_WAIT; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | 9 | import android.content.DialogInterface; 10 | import android.os.Bundle; 11 | import android.os.Environment; 12 | import android.view.View; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | 16 | import com.ixuea.android.common.activity.BaseActivity; 17 | import com.ixuea.android.downloader.DownloadService; 18 | import com.ixuea.android.downloader.callback.DownloadListener; 19 | import com.ixuea.android.downloader.callback.DownloadManager; 20 | import com.ixuea.android.downloader.domain.DownloadInfo; 21 | import com.ixuea.android.downloader.exception.DownloadException; 22 | import com.ixuea.android.downloader.simple.R; 23 | import com.ixuea.android.downloader.simple.util.FileUtil; 24 | 25 | import java.io.File; 26 | 27 | 28 | /** 29 | * How to download a file sample. 30 | */ 31 | public class SimpleActivity extends BaseActivity implements View.OnClickListener { 32 | 33 | public static final String DEFAULT_URL = "https://3b8637d9f6c334dab555e2afbdc16687.dlied1.cdntips.net/imtt.dd.qq.com/16891/apk/49F7A4E3B47E5828D02B2A10C580DB65.apk"; 34 | 35 | 36 | private TextView tv_download_info; 37 | private Button bt_download_button; 38 | private DownloadManager downloadManager; 39 | private DownloadInfo downloadInfo; 40 | 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_simple); 46 | 47 | 48 | } 49 | 50 | @Override 51 | public void initListener() { 52 | bt_download_button.setOnClickListener(this); 53 | } 54 | 55 | @Override 56 | public void initData() { 57 | downloadManager = DownloadService.getDownloadManager(getApplicationContext()); 58 | 59 | downloadInfo = downloadManager.getDownloadById(DEFAULT_URL); 60 | 61 | if (downloadInfo != null) { 62 | setDownloadListener(); 63 | } 64 | 65 | 66 | refresh(); 67 | } 68 | 69 | private void refresh() { 70 | if (downloadInfo == null) { 71 | bt_download_button.setText("Download"); 72 | tv_download_info.setText(""); 73 | return; 74 | } 75 | switch (downloadInfo.getStatus()) { 76 | case DownloadInfo.STATUS_NONE: 77 | bt_download_button.setText("Download"); 78 | tv_download_info.setText(""); 79 | break; 80 | case DownloadInfo.STATUS_PAUSED: 81 | bt_download_button.setText("Continue"); 82 | tv_download_info.setText("Paused"); 83 | break; 84 | case DownloadInfo.STATUS_ERROR: 85 | bt_download_button.setText("Continue"); 86 | 87 | String errorMessage=""; 88 | if (downloadInfo.getException() != null) { 89 | errorMessage=downloadInfo.getException().getLocalizedMessage(); 90 | } 91 | tv_download_info.setText("Download fail:"+errorMessage); 92 | break; 93 | 94 | case DownloadInfo.STATUS_DOWNLOADING: 95 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 96 | tv_download_info 97 | .setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 98 | .formatFileSize(downloadInfo.getSize())); 99 | bt_download_button.setText("Pause"); 100 | break; 101 | case STATUS_COMPLETED: 102 | bt_download_button.setText("Delete"); 103 | tv_download_info.setText("Download success"); 104 | break; 105 | case STATUS_REMOVED: 106 | bt_download_button.setText("Download"); 107 | tv_download_info.setText(""); 108 | downloadInfo = null; 109 | case STATUS_WAIT: 110 | tv_download_info.setText("Waiting"); 111 | bt_download_button.setText("Pause"); 112 | break; 113 | } 114 | } 115 | 116 | 117 | private void createDownload() { 118 | File d = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "d"); 119 | if (!d.exists()) { 120 | d.mkdirs(); 121 | } 122 | String path = d.getAbsolutePath().concat("/").concat("a.apk"); 123 | downloadInfo = new DownloadInfo.Builder().setUrl(DEFAULT_URL) 124 | .setPath(path) 125 | .build(); 126 | setDownloadListener(); 127 | downloadManager.download(downloadInfo); 128 | } 129 | 130 | private void setDownloadListener() { 131 | downloadInfo.setDownloadListener(new DownloadListener() { 132 | 133 | @Override 134 | public void onStart() { 135 | tv_download_info.setText("Prepare downloading"); 136 | } 137 | 138 | @Override 139 | public void onWaited() { 140 | tv_download_info.setText("Waiting"); 141 | bt_download_button.setText("Pause"); 142 | } 143 | 144 | @Override 145 | public void onPaused() { 146 | bt_download_button.setText("Continue"); 147 | tv_download_info.setText("Paused"); 148 | } 149 | 150 | @Override 151 | public void onDownloading(long progress, long size) { 152 | tv_download_info 153 | .setText(FileUtil.formatFileSize(progress) + "/" + FileUtil 154 | .formatFileSize(size)); 155 | bt_download_button.setText("Pause"); 156 | } 157 | 158 | @Override 159 | public void onRemoved() { 160 | bt_download_button.setText("Download"); 161 | tv_download_info.setText(""); 162 | downloadInfo = null; 163 | } 164 | 165 | @Override 166 | public void onDownloadSuccess() { 167 | bt_download_button.setText("Delete"); 168 | tv_download_info.setText("Download success"); 169 | } 170 | 171 | @Override 172 | public void onDownloadFailed(DownloadException e) { 173 | bt_download_button.setText("Continue"); 174 | tv_download_info.setText("Download fail:" + e.getMessage()); 175 | } 176 | }); 177 | } 178 | 179 | @Override 180 | public void initView() { 181 | tv_download_info = findViewById(R.id.tv_download_info); 182 | bt_download_button = findViewById(R.id.bt_download_button); 183 | 184 | } 185 | 186 | @Override 187 | public void onClick(View v) { 188 | if (downloadInfo == null) { 189 | createDownload(); 190 | } else { 191 | switch (downloadInfo.getStatus()) { 192 | case DownloadInfo.STATUS_NONE: 193 | case DownloadInfo.STATUS_PAUSED: 194 | case DownloadInfo.STATUS_ERROR: 195 | 196 | //resume downloadInfo 197 | downloadManager.resume(downloadInfo); 198 | break; 199 | 200 | case DownloadInfo.STATUS_DOWNLOADING: 201 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 202 | case STATUS_WAIT: 203 | //pause downloadInfo 204 | downloadManager.pause(downloadInfo); 205 | break; 206 | case STATUS_COMPLETED: 207 | downloadManager.remove(downloadInfo); 208 | break; 209 | } 210 | } 211 | 212 | } 213 | 214 | 215 | } 216 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/adapter/DownloadAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.View.OnClickListener; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.ImageView; 10 | import android.widget.ProgressBar; 11 | import android.widget.TextView; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.ixuea.android.common.adapter.BaseRecyclerViewAdapter; 15 | import com.ixuea.android.downloader.domain.DownloadInfo; 16 | import com.ixuea.android.downloader.simple.R; 17 | import com.ixuea.android.downloader.simple.callback.MyDownloadListener; 18 | import com.ixuea.android.downloader.simple.db.DBController; 19 | import com.ixuea.android.downloader.simple.domain.MyBusinessInfLocal; 20 | import com.ixuea.android.downloader.simple.event.DownloadStatusChanged; 21 | import com.ixuea.android.downloader.simple.util.FileUtil; 22 | 23 | import org.greenrobot.eventbus.EventBus; 24 | 25 | import java.lang.ref.SoftReference; 26 | import java.sql.SQLException; 27 | 28 | import static com.ixuea.android.downloader.DownloadService.downloadManager; 29 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_COMPLETED; 30 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_REMOVED; 31 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_WAIT; 32 | 33 | import androidx.recyclerview.widget.RecyclerView; 34 | 35 | /** 36 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 37 | */ 38 | 39 | public class DownloadAdapter extends 40 | BaseRecyclerViewAdapter { 41 | 42 | private DBController dbController; 43 | 44 | public DownloadAdapter(Context context) { 45 | super(context); 46 | try { 47 | dbController = DBController.getInstance(context.getApplicationContext()); 48 | } catch (SQLException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | @Override 54 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 55 | return new DownloadAdapter.ViewHolder(LayoutInflater.from(context).inflate( 56 | R.layout.item_download_info, parent, false)); 57 | } 58 | 59 | @Override 60 | public void onBindViewHolder(ViewHolder holder, int position) { 61 | 62 | DownloadInfo data = getData(position); 63 | try { 64 | MyBusinessInfLocal myDownloadInfoById = dbController 65 | .findMyDownloadInfoById(data.getUri()); 66 | if (myDownloadInfoById != null) { 67 | holder.bindBaseInfo(myDownloadInfoById); 68 | } 69 | 70 | } catch (SQLException e) { 71 | e.printStackTrace(); 72 | } 73 | 74 | holder.bindData(data, position, context); 75 | 76 | // holder.itemView.setOnClickListener(new OnClickListener() { 77 | // @Override 78 | // public void onClick(View v) { 79 | // if (onItemClickListener != null) { 80 | // onItemClickListener.onItemClick(position); 81 | // } 82 | // } 83 | // }); 84 | } 85 | 86 | class ViewHolder extends RecyclerView.ViewHolder { 87 | 88 | private final ImageView iv_icon; 89 | private final TextView tv_size; 90 | private final TextView tv_status; 91 | private final ProgressBar pb; 92 | private final TextView tv_name; 93 | private final Button bt_action; 94 | private DownloadInfo downloadInfo; 95 | 96 | public ViewHolder(View view) { 97 | super(view); 98 | itemView.setClickable(true); 99 | iv_icon = (ImageView) view.findViewById(R.id.iv_icon); 100 | tv_size = (TextView) view.findViewById(R.id.tv_size); 101 | tv_status = (TextView) view.findViewById(R.id.tv_status); 102 | pb = (ProgressBar) view.findViewById(R.id.pb); 103 | tv_name = (TextView) view.findViewById(R.id.tv_name); 104 | bt_action = (Button) view.findViewById(R.id.bt_action); 105 | } 106 | 107 | @SuppressWarnings("unchecked") 108 | public void bindData(final DownloadInfo data, int position, final Context context) { 109 | // Glide.with(context).load(data.getIcon()).into(iv_icon); 110 | // tv_name.setText(data.getName()); 111 | 112 | // Get download task status. 113 | downloadInfo = data; 114 | 115 | // Set a download listener 116 | if (downloadInfo != null) { 117 | downloadInfo 118 | .setDownloadListener( 119 | new MyDownloadListener(new SoftReference(DownloadAdapter.ViewHolder.this)) { 120 | // Call interval about one second. 121 | @Override 122 | public void onRefresh() { 123 | notifyDownloadStatus(); 124 | 125 | if (getUserTag() != null && getUserTag().get() != null) { 126 | DownloadAdapter.ViewHolder viewHolder = (DownloadAdapter.ViewHolder) getUserTag() 127 | .get(); 128 | viewHolder.refresh(); 129 | } 130 | } 131 | }); 132 | 133 | } 134 | 135 | refresh(); 136 | 137 | // Download button 138 | bt_action.setOnClickListener(new OnClickListener() { 139 | @Override 140 | public void onClick(View v) { 141 | if (downloadInfo != null) { 142 | 143 | switch (downloadInfo.getStatus()) { 144 | case DownloadInfo.STATUS_NONE: 145 | case DownloadInfo.STATUS_PAUSED: 146 | case DownloadInfo.STATUS_ERROR: 147 | 148 | //resume downloadInfo 149 | downloadManager.resume(downloadInfo); 150 | break; 151 | 152 | case DownloadInfo.STATUS_DOWNLOADING: 153 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 154 | case STATUS_WAIT: 155 | //pause downloadInfo 156 | downloadManager.pause(downloadInfo); 157 | break; 158 | case DownloadInfo.STATUS_COMPLETED: 159 | downloadManager.remove(downloadInfo); 160 | break; 161 | } 162 | } 163 | } 164 | }); 165 | 166 | } 167 | 168 | private void refresh() { 169 | if (downloadInfo == null) { 170 | tv_size.setText(""); 171 | pb.setProgress(0); 172 | bt_action.setText("Download"); 173 | tv_status.setText("not downloadInfo"); 174 | } else { 175 | switch (downloadInfo.getStatus()) { 176 | case DownloadInfo.STATUS_NONE: 177 | bt_action.setText("Download"); 178 | tv_status.setText("not downloadInfo"); 179 | break; 180 | case DownloadInfo.STATUS_PAUSED: 181 | case DownloadInfo.STATUS_ERROR: 182 | bt_action.setText("Continue"); 183 | tv_status.setText("paused"); 184 | try { 185 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 186 | } catch (Exception e) { 187 | e.printStackTrace(); 188 | } 189 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 190 | .formatFileSize(downloadInfo.getSize())); 191 | break; 192 | 193 | case DownloadInfo.STATUS_DOWNLOADING: 194 | case DownloadInfo.STATUS_PREPARE_DOWNLOAD: 195 | bt_action.setText("Pause"); 196 | try { 197 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 198 | } catch (Exception e) { 199 | e.printStackTrace(); 200 | } 201 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 202 | .formatFileSize(downloadInfo.getSize())); 203 | tv_status.setText("downloading"); 204 | break; 205 | case STATUS_COMPLETED: 206 | bt_action.setText("Delete"); 207 | try { 208 | pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); 209 | } catch (Exception e) { 210 | e.printStackTrace(); 211 | } 212 | tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil 213 | .formatFileSize(downloadInfo.getSize())); 214 | tv_status.setText("success"); 215 | 216 | publishDownloadSuccessStatus(); 217 | break; 218 | case STATUS_REMOVED: 219 | tv_size.setText(""); 220 | pb.setProgress(0); 221 | bt_action.setText("Download"); 222 | tv_status.setText("not downloadInfo"); 223 | 224 | publishDownloadSuccessStatus(); 225 | case STATUS_WAIT: 226 | tv_size.setText(""); 227 | pb.setProgress(0); 228 | bt_action.setText("Pause"); 229 | tv_status.setText("Waiting"); 230 | break; 231 | } 232 | 233 | } 234 | } 235 | 236 | private void publishDownloadSuccessStatus() { 237 | //publish download success info. 238 | EventBus.getDefault().post(new DownloadStatusChanged(downloadInfo)); 239 | } 240 | 241 | public void bindBaseInfo(MyBusinessInfLocal myBusinessInfLocal) { 242 | Glide.with(context).load(myBusinessInfLocal.getIcon()).into(iv_icon); 243 | tv_name.setText(myBusinessInfLocal.getName()); 244 | } 245 | 246 | private void notifyDownloadStatus() { 247 | 248 | if (downloadInfo.getStatus() == STATUS_REMOVED) { 249 | try { 250 | dbController.deleteMyDownloadInfo(downloadInfo.getUri()); 251 | } catch (SQLException e) { 252 | e.printStackTrace(); 253 | } 254 | } 255 | } 256 | 257 | 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/adapter/DownloadManagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.fragment.app.Fragment; 6 | import androidx.fragment.app.FragmentManager; 7 | 8 | import com.ixuea.android.common.adapter.CustomFragmentPagerAdapter; 9 | import com.ixuea.android.downloader.simple.fragment.DownloadedFragment; 10 | import com.ixuea.android.downloader.simple.fragment.DownloadingFragment; 11 | 12 | /** 13 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 14 | */ 15 | 16 | public class DownloadManagerAdapter extends CustomFragmentPagerAdapter { 17 | 18 | 19 | public DownloadManagerAdapter(FragmentManager fm, Context context) { 20 | super(fm, context); 21 | } 22 | 23 | @Override 24 | public Fragment getItem(int position) { 25 | if (position == 0) { 26 | return DownloadingFragment.newInstance(); 27 | } else { 28 | return DownloadedFragment.newInstance(); 29 | } 30 | } 31 | 32 | @Override 33 | public CharSequence getPageTitle(int position) { 34 | return getData(position); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/callback/MyDownloadListener.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.callback; 2 | 3 | import com.ixuea.android.downloader.callback.AbsDownloadListener; 4 | import com.ixuea.android.downloader.exception.DownloadException; 5 | 6 | import java.lang.ref.SoftReference; 7 | 8 | /** 9 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 10 | */ 11 | 12 | public abstract class MyDownloadListener extends AbsDownloadListener { 13 | 14 | public MyDownloadListener() { 15 | super(); 16 | } 17 | 18 | public MyDownloadListener(SoftReference userTag) { 19 | super(userTag); 20 | } 21 | 22 | @Override 23 | public void onStart() { 24 | onRefresh(); 25 | } 26 | 27 | public abstract void onRefresh(); 28 | 29 | @Override 30 | public void onWaited() { 31 | onRefresh(); 32 | } 33 | 34 | @Override 35 | public void onDownloading(long progress, long size) { 36 | onRefresh(); 37 | } 38 | 39 | @Override 40 | public void onRemoved() { 41 | onRefresh(); 42 | } 43 | 44 | @Override 45 | public void onDownloadSuccess() { 46 | onRefresh(); 47 | } 48 | 49 | @Override 50 | public void onDownloadFailed(DownloadException e) { 51 | onRefresh(); 52 | } 53 | 54 | @Override 55 | public void onPaused() { 56 | onRefresh(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/db/DBController.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.db; 2 | 3 | import android.content.Context; 4 | 5 | import com.ixuea.android.downloader.db.DownloadDBController; 6 | import com.ixuea.android.downloader.domain.DownloadInfo; 7 | import com.ixuea.android.downloader.domain.DownloadThreadInfo; 8 | import com.ixuea.android.downloader.simple.domain.MyBusinessInfLocal; 9 | import com.ixuea.android.downloader.simple.domain.MyDownloadInfLocal; 10 | import com.ixuea.android.downloader.simple.domain.MyDownloadThreadInfoLocal; 11 | import com.j256.ormlite.dao.Dao; 12 | import com.j256.ormlite.stmt.UpdateBuilder; 13 | 14 | import java.sql.SQLException; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_COMPLETED; 19 | import static com.ixuea.android.downloader.domain.DownloadInfo.STATUS_PAUSED; 20 | 21 | /** 22 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 23 | */ 24 | 25 | public class DBController implements DownloadDBController { 26 | 27 | private static DBController instance; 28 | private final Context context; 29 | private final DBHelper dbHelper; 30 | private final Dao myBusinessInfoLocalsDao; 31 | private final Dao myDownloadInfLocalDao; 32 | private final Dao myDownloadThreadInfoLocalDao; 33 | 34 | public DBController(Context context) throws SQLException { 35 | this.context = context; 36 | dbHelper = new DBHelper(context); 37 | try { 38 | myBusinessInfoLocalsDao = dbHelper.getDao(MyBusinessInfLocal.class); 39 | myDownloadInfLocalDao = dbHelper.getDao(MyDownloadInfLocal.class); 40 | myDownloadThreadInfoLocalDao = dbHelper.getDao(MyDownloadThreadInfoLocal.class); 41 | } catch (SQLException e) { 42 | e.printStackTrace(); 43 | throw e; 44 | } 45 | } 46 | 47 | public static DBController getInstance(Context context) throws SQLException { 48 | if (instance == null) { 49 | instance = new DBController(context); 50 | } 51 | return instance; 52 | } 53 | 54 | 55 | public void createOrUpdateMyDownloadInfo(MyBusinessInfLocal downloadInfoLocal) 56 | throws SQLException { 57 | myBusinessInfoLocalsDao.createOrUpdate(downloadInfoLocal); 58 | } 59 | 60 | public int deleteMyDownloadInfo(String id) 61 | throws SQLException { 62 | return myBusinessInfoLocalsDao.deleteById(id); 63 | } 64 | 65 | public MyBusinessInfLocal findMyDownloadInfoById(String id) 66 | throws SQLException { 67 | return myBusinessInfoLocalsDao.queryForId(id); 68 | } 69 | 70 | @Override 71 | public List findAllDownloading() { 72 | try { 73 | List myDownloadInfLocals = myDownloadInfLocalDao.queryBuilder().where() 74 | .ne("status", STATUS_COMPLETED).query(); 75 | return convertDownloadInfos(myDownloadInfLocals); 76 | } catch (SQLException e) { 77 | e.printStackTrace(); 78 | } 79 | return new ArrayList<>(); 80 | } 81 | 82 | 83 | @Override 84 | public List findAllDownloaded() { 85 | try { 86 | List myDownloadInfLocals = myDownloadInfLocalDao.queryBuilder().where() 87 | .eq("status", STATUS_COMPLETED).query(); 88 | return convertDownloadInfos(myDownloadInfLocals); 89 | } catch (SQLException e) { 90 | e.printStackTrace(); 91 | } 92 | return new ArrayList<>(); 93 | } 94 | 95 | @Override 96 | public DownloadInfo findDownloadedInfoById(String id) { 97 | try { 98 | return convertDownloadInfo(myDownloadInfLocalDao.queryForId(id)); 99 | } catch (SQLException e) { 100 | e.printStackTrace(); 101 | } 102 | return null; 103 | } 104 | 105 | @Override 106 | public void pauseAllDownloading() { 107 | 108 | try { 109 | UpdateBuilder myDownloadInfLocalIntegerUpdateBuilder = myDownloadInfLocalDao 110 | .updateBuilder(); 111 | myDownloadInfLocalIntegerUpdateBuilder.updateColumnValue("status", STATUS_PAUSED).where() 112 | .ne("status", STATUS_COMPLETED); 113 | myDownloadInfLocalIntegerUpdateBuilder.update(); 114 | } catch (SQLException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | 119 | @Override 120 | public void createOrUpdate(DownloadInfo downloadInfo) { 121 | try { 122 | myDownloadInfLocalDao.createOrUpdate(convertDownloadInfo(downloadInfo)); 123 | } catch (SQLException e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | 128 | 129 | @Override 130 | public void createOrUpdate(DownloadThreadInfo downloadThreadInfo) { 131 | try { 132 | myDownloadThreadInfoLocalDao.createOrUpdate(convertDownloadThreadInfo(downloadThreadInfo)); 133 | } catch (SQLException e) { 134 | e.printStackTrace(); 135 | } 136 | } 137 | 138 | @Override 139 | public void delete(DownloadInfo downloadInfo) { 140 | try { 141 | myDownloadInfLocalDao.deleteById(downloadInfo.getId()); 142 | } catch (SQLException e) { 143 | e.printStackTrace(); 144 | } 145 | } 146 | 147 | @Override 148 | public void delete(DownloadThreadInfo download) { 149 | try { 150 | myDownloadThreadInfoLocalDao.deleteById(download.getDownloadInfoId()); 151 | } catch (SQLException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | private List convertDownloadInfos(List infos) { 157 | List downloadInfos = new ArrayList<>(); 158 | for (MyDownloadInfLocal downloadInfLocal : infos) { 159 | downloadInfos.add(convertDownloadInfo(downloadInfLocal)); 160 | } 161 | return downloadInfos; 162 | } 163 | 164 | 165 | private DownloadInfo convertDownloadInfo(MyDownloadInfLocal downloadInfLocal) { 166 | if (downloadInfLocal == null) { 167 | return null; 168 | } 169 | DownloadInfo downloadInfo = new DownloadInfo(); 170 | downloadInfo.setCreateAt(downloadInfLocal.getCreateAt()); 171 | downloadInfo.setUri(downloadInfLocal.getUri()); 172 | downloadInfo.setPath(downloadInfLocal.getPath()); 173 | downloadInfo.setSize(downloadInfLocal.getSize()); 174 | downloadInfo.setProgress(downloadInfLocal.getProgress()); 175 | downloadInfo.setStatus(downloadInfLocal.getStatus()); 176 | downloadInfo.setSupportRanges(downloadInfLocal.getSupportRanges()); 177 | downloadInfo.setDownloadThreadInfos( 178 | converDownloadThreadInfos1(downloadInfLocal.getDownloadThreadInfos())); 179 | return downloadInfo; 180 | } 181 | 182 | private List converDownloadThreadInfos1( 183 | List downloadThreadInfosLocal) { 184 | List downloadThreadInfos = new ArrayList<>(); 185 | if (downloadThreadInfosLocal != null) { 186 | for (MyDownloadThreadInfoLocal d : downloadThreadInfosLocal 187 | ) { 188 | downloadThreadInfos.add(convertDownloadThreadInfo(d)); 189 | } 190 | } 191 | 192 | return downloadThreadInfos; 193 | } 194 | 195 | private List convertDownloadThreadInfos( 196 | List downloadThreadInfos) { 197 | if (downloadThreadInfos == null) { 198 | return null; 199 | } 200 | List downloadThreadInfosLocal = new ArrayList<>(); 201 | for (DownloadThreadInfo d : downloadThreadInfos 202 | ) { 203 | downloadThreadInfosLocal.add(convertDownloadThreadInfo(d)); 204 | } 205 | return downloadThreadInfosLocal; 206 | } 207 | 208 | 209 | private DownloadThreadInfo convertDownloadThreadInfo(MyDownloadThreadInfoLocal d) { 210 | DownloadThreadInfo downloadThreadInfo = new DownloadThreadInfo(); 211 | downloadThreadInfo.setProgress(d.getId()); 212 | downloadThreadInfo.setThreadId(d.getThreadId()); 213 | downloadThreadInfo.setDownloadInfoId(d.getDownloadInfoId()); 214 | downloadThreadInfo.setUri(d.getUri()); 215 | downloadThreadInfo.setStart(d.getStart()); 216 | downloadThreadInfo.setEnd(d.getEnd()); 217 | downloadThreadInfo.setProgress(d.getProgress()); 218 | return downloadThreadInfo; 219 | } 220 | 221 | private MyDownloadThreadInfoLocal convertDownloadThreadInfo(DownloadThreadInfo d) { 222 | MyDownloadThreadInfoLocal downloadThreadInfo = new MyDownloadThreadInfoLocal(); 223 | downloadThreadInfo.setProgress(d.getId()); 224 | downloadThreadInfo.setThreadId(d.getThreadId()); 225 | downloadThreadInfo.setDownloadInfoId(d.getDownloadInfoId()); 226 | downloadThreadInfo.setUri(d.getUri()); 227 | downloadThreadInfo.setStart(d.getStart()); 228 | downloadThreadInfo.setEnd(d.getEnd()); 229 | downloadThreadInfo.setProgress(d.getProgress()); 230 | return downloadThreadInfo; 231 | } 232 | 233 | private MyDownloadInfLocal convertDownloadInfo(DownloadInfo downloadInfo) { 234 | MyDownloadInfLocal downloadInfLocal = new MyDownloadInfLocal(); 235 | downloadInfLocal.setCreateAt(downloadInfo.getCreateAt()); 236 | downloadInfLocal.setUri(downloadInfo.getUri()); 237 | downloadInfLocal.setPath(downloadInfo.getPath()); 238 | downloadInfLocal.setSize(downloadInfo.getSize()); 239 | downloadInfLocal.setProgress(downloadInfo.getProgress()); 240 | downloadInfLocal.setStatus(downloadInfo.getStatus()); 241 | downloadInfLocal.setSupportRanges(downloadInfo.getSupportRanges()); 242 | downloadInfLocal 243 | .setDownloadThreadInfos(convertDownloadThreadInfos(downloadInfo.getDownloadThreadInfos())); 244 | return downloadInfLocal; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/db/DBHelper.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.db; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | 6 | import com.ixuea.android.downloader.simple.domain.MyBusinessInfLocal; 7 | import com.ixuea.android.downloader.simple.domain.MyDownloadInfLocal; 8 | import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; 9 | import com.j256.ormlite.support.ConnectionSource; 10 | import com.j256.ormlite.table.TableUtils; 11 | 12 | import java.sql.SQLException; 13 | 14 | /** 15 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 16 | */ 17 | 18 | public class DBHelper extends OrmLiteSqliteOpenHelper { 19 | 20 | // private static final String DB_NAME = "/sdcard/d/data.db"; 21 | private static final String DB_NAME = "download_data.db"; 22 | private static final int DB_VERSION = 3; 23 | 24 | public DBHelper(Context context) { 25 | super(context, DB_NAME, null, DB_VERSION); 26 | } 27 | 28 | @Override 29 | public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { 30 | try { 31 | TableUtils.createTable(connectionSource, MyBusinessInfLocal.class); 32 | TableUtils.createTable(connectionSource, MyDownloadInfLocal.class); 33 | } catch (SQLException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | 38 | @Override 39 | public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, 40 | int newVersion) { 41 | try { 42 | TableUtils.dropTable(connectionSource, MyBusinessInfLocal.class, true); 43 | TableUtils.dropTable(connectionSource, MyDownloadInfLocal.class, true); 44 | } catch (SQLException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/domain/MyBusinessInfLocal.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.domain; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 8 | */ 9 | @DatabaseTable(tableName = "MyBusinessInfLocal") 10 | public class MyBusinessInfLocal { 11 | 12 | @DatabaseField(id = true) 13 | private String id; 14 | 15 | @DatabaseField 16 | private String name; 17 | 18 | @DatabaseField 19 | private String icon; 20 | 21 | @DatabaseField 22 | private String url; 23 | 24 | public MyBusinessInfLocal() { 25 | } 26 | 27 | public MyBusinessInfLocal(String id, String name, String icon, String url) { 28 | this.id = id; 29 | this.name = name; 30 | this.icon = icon; 31 | this.url = url; 32 | } 33 | 34 | public String getId() { 35 | return id; 36 | } 37 | 38 | public void setId(String id) { 39 | this.id = id; 40 | } 41 | 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public void setName(String name) { 47 | this.name = name; 48 | } 49 | 50 | public String getIcon() { 51 | return icon; 52 | } 53 | 54 | public void setIcon(String icon) { 55 | this.icon = icon; 56 | } 57 | 58 | public String getUrl() { 59 | return url; 60 | } 61 | 62 | public void setUrl(String url) { 63 | this.url = url; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/domain/MyBusinessInfo.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 7 | */ 8 | public class MyBusinessInfo implements Serializable { 9 | 10 | private String name; 11 | private String icon; 12 | private String url; 13 | 14 | public MyBusinessInfo(String name, String icon, String url) { 15 | this.name = name; 16 | this.icon = icon; 17 | this.url = url; 18 | } 19 | 20 | public String getIcon() { 21 | return icon; 22 | } 23 | 24 | public void setIcon(String icon) { 25 | this.icon = icon; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getUrl() { 37 | return url; 38 | } 39 | 40 | public void setUrl(String url) { 41 | this.url = url; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/domain/MyDownloadInfLocal.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.domain; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 10 | */ 11 | @DatabaseTable(tableName = "MyDownloadInfLocal") 12 | public class MyDownloadInfLocal { 13 | 14 | @DatabaseField(id = true) 15 | private int id; 16 | 17 | @DatabaseField 18 | private long createAt; 19 | 20 | @DatabaseField 21 | private String uri; 22 | 23 | @DatabaseField 24 | private String path; 25 | 26 | @DatabaseField 27 | private long size; 28 | 29 | @DatabaseField 30 | private long progress; 31 | 32 | @DatabaseField 33 | private int status; 34 | 35 | @DatabaseField 36 | private int supportRanges; 37 | 38 | private List downloadThreadInfos; 39 | 40 | public int getId() { 41 | return id; 42 | } 43 | 44 | public void setId(int id) { 45 | this.id = id; 46 | } 47 | 48 | public long getCreateAt() { 49 | return createAt; 50 | } 51 | 52 | public void setCreateAt(long createAt) { 53 | this.createAt = createAt; 54 | } 55 | 56 | public String getUri() { 57 | return uri; 58 | } 59 | 60 | public void setUri(String uri) { 61 | this.uri = uri; 62 | } 63 | 64 | public String getPath() { 65 | return path; 66 | } 67 | 68 | public void setPath(String path) { 69 | this.path = path; 70 | } 71 | 72 | public long getSize() { 73 | return size; 74 | } 75 | 76 | public void setSize(long size) { 77 | this.size = size; 78 | } 79 | 80 | public long getProgress() { 81 | return progress; 82 | } 83 | 84 | public void setProgress(long progress) { 85 | this.progress = progress; 86 | } 87 | 88 | public int getStatus() { 89 | return status; 90 | } 91 | 92 | public void setStatus(int status) { 93 | this.status = status; 94 | } 95 | 96 | public List getDownloadThreadInfos() { 97 | return downloadThreadInfos; 98 | } 99 | 100 | public void setDownloadThreadInfos( 101 | List downloadThreadInfos) { 102 | this.downloadThreadInfos = downloadThreadInfos; 103 | } 104 | 105 | public int getSupportRanges() { 106 | return supportRanges; 107 | } 108 | 109 | public void setSupportRanges(int supportRanges) { 110 | this.supportRanges = supportRanges; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/domain/MyDownloadThreadInfoLocal.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.domain; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 10 | */ 11 | 12 | @DatabaseTable(tableName = "MyDownloadThreadInfoLocal") 13 | public class MyDownloadThreadInfoLocal implements Serializable { 14 | 15 | @DatabaseField(id = true) 16 | private int id; 17 | 18 | @DatabaseField 19 | private int threadId; 20 | 21 | @DatabaseField 22 | private String downloadInfoId; 23 | private String uri; 24 | 25 | @DatabaseField 26 | private long start; 27 | 28 | @DatabaseField 29 | private long end; 30 | 31 | @DatabaseField 32 | private long progress; 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 int getThreadId() { 44 | return threadId; 45 | } 46 | 47 | public void setThreadId(int threadId) { 48 | this.threadId = threadId; 49 | } 50 | 51 | public String getDownloadInfoId() { 52 | return downloadInfoId; 53 | } 54 | 55 | public void setDownloadInfoId(String downloadInfoId) { 56 | this.downloadInfoId = downloadInfoId; 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 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/event/DownloadStatusChanged.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.event; 2 | 3 | import com.ixuea.android.downloader.domain.DownloadInfo; 4 | 5 | /** 6 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 7 | */ 8 | 9 | public class DownloadStatusChanged { 10 | 11 | public DownloadStatusChanged(DownloadInfo downloadInfo) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/fragment/DownloadedFragment.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.recyclerview.widget.LinearLayoutManager; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.ixuea.android.common.fragment.BaseFragment; 12 | import com.ixuea.android.downloader.DownloadService; 13 | import com.ixuea.android.downloader.callback.DownloadManager; 14 | import com.ixuea.android.downloader.simple.R; 15 | import com.ixuea.android.downloader.simple.adapter.DownloadAdapter; 16 | import com.ixuea.android.downloader.simple.event.DownloadStatusChanged; 17 | 18 | import org.greenrobot.eventbus.EventBus; 19 | import org.greenrobot.eventbus.Subscribe; 20 | 21 | /** 22 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 23 | */ 24 | 25 | public class DownloadedFragment extends BaseFragment { 26 | 27 | private RecyclerView rv; 28 | private DownloadAdapter downloadAdapter; 29 | private DownloadManager downloadManager; 30 | 31 | public static DownloadedFragment newInstance() { 32 | 33 | Bundle args = new Bundle(); 34 | 35 | DownloadedFragment fragment = new DownloadedFragment(); 36 | fragment.setArguments(args); 37 | return fragment; 38 | } 39 | 40 | @Override 41 | protected View getLayoutView(LayoutInflater inflater, ViewGroup container, 42 | Bundle savedInstanceState) { 43 | return inflater.inflate(R.layout.fragment_downloaded, null); 44 | } 45 | 46 | @Override 47 | protected void initView() { 48 | super.initView(); 49 | rv = (RecyclerView) getView().findViewById(R.id.rv); 50 | rv.setLayoutManager(new LinearLayoutManager(getActivity())); 51 | 52 | 53 | } 54 | 55 | @Override 56 | protected void initData() { 57 | super.initData(); 58 | EventBus.getDefault().register(this); 59 | 60 | downloadManager = DownloadService 61 | .getDownloadManager(getActivity().getApplicationContext()); 62 | 63 | downloadAdapter = new DownloadAdapter(getActivity()); 64 | rv.setAdapter(downloadAdapter); 65 | 66 | //downloading info 67 | 68 | fetchData(); 69 | } 70 | 71 | private void fetchData() { 72 | downloadAdapter.setData(downloadManager.findAllDownloaded()); 73 | } 74 | 75 | @Subscribe 76 | public void onEventMainThread(DownloadStatusChanged event) { 77 | fetchData(); 78 | } 79 | 80 | @Override 81 | public void onDestroy() { 82 | EventBus.getDefault().unregister(this); 83 | super.onDestroy(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/fragment/DownloadingFragment.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.Button; 8 | import android.widget.Toast; 9 | 10 | import androidx.recyclerview.widget.LinearLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import com.ixuea.android.common.fragment.BaseFragment; 14 | import com.ixuea.android.downloader.DownloadService; 15 | import com.ixuea.android.downloader.callback.DownloadManager; 16 | import com.ixuea.android.downloader.domain.DownloadInfo; 17 | import com.ixuea.android.downloader.simple.R; 18 | import com.ixuea.android.downloader.simple.adapter.DownloadAdapter; 19 | import com.ixuea.android.downloader.simple.event.DownloadStatusChanged; 20 | 21 | import org.greenrobot.eventbus.EventBus; 22 | import org.greenrobot.eventbus.Subscribe; 23 | 24 | /** 25 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 26 | */ 27 | 28 | public class DownloadingFragment extends BaseFragment implements View.OnClickListener { 29 | 30 | private RecyclerView rv; 31 | private DownloadAdapter downloadAdapter; 32 | private DownloadManager downloadManager; 33 | private Button bt_control_all; 34 | private Button bt_clear_all; 35 | private boolean hasDownloading; 36 | 37 | public static DownloadingFragment newInstance() { 38 | 39 | Bundle args = new Bundle(); 40 | 41 | DownloadingFragment fragment = new DownloadingFragment(); 42 | fragment.setArguments(args); 43 | return fragment; 44 | } 45 | 46 | @Override 47 | protected View getLayoutView(LayoutInflater inflater, ViewGroup container, 48 | Bundle savedInstanceState) { 49 | return inflater.inflate(R.layout.fragment_downloading, null); 50 | } 51 | 52 | @Override 53 | protected void initView() { 54 | super.initView(); 55 | rv = (RecyclerView) getView().findViewById(R.id.rv); 56 | rv.setLayoutManager(new LinearLayoutManager(getActivity())); 57 | 58 | bt_control_all = (Button) getView().findViewById(R.id.bt_control_all); 59 | bt_clear_all = (Button) getView().findViewById(R.id.bt_clear_all); 60 | } 61 | 62 | @Override 63 | protected void initData() { 64 | super.initData(); 65 | EventBus.getDefault().register(this); 66 | downloadManager = DownloadService 67 | .getDownloadManager(getActivity().getApplicationContext()); 68 | 69 | downloadAdapter = new DownloadAdapter(getActivity()); 70 | rv.setAdapter(downloadAdapter); 71 | 72 | fetchData(); 73 | } 74 | 75 | @Override 76 | protected void initListener() { 77 | super.initListener(); 78 | bt_control_all.setOnClickListener(this); 79 | bt_clear_all.setOnClickListener(this); 80 | } 81 | 82 | @Subscribe 83 | public void onEventMainThread(DownloadStatusChanged event) { 84 | fetchData(); 85 | } 86 | 87 | private void fetchData() { 88 | downloadAdapter.setData(downloadManager.findAllDownloading()); 89 | setPauseOrResumeButtonStatus(); 90 | } 91 | 92 | @Override 93 | public void onDestroy() { 94 | EventBus.getDefault().unregister(this); 95 | super.onDestroy(); 96 | } 97 | 98 | @Override 99 | public void onClick(View view) { 100 | switch (view.getId()) { 101 | case R.id.bt_control_all: 102 | 103 | onPauseAllClick(); 104 | break; 105 | case R.id.bt_clear_all: 106 | onDeleteAllClick(); 107 | break; 108 | 109 | } 110 | } 111 | 112 | private void onDeleteAllClick() { 113 | if (downloadAdapter.getData().size() == 0) { 114 | Toast.makeText(getActivity(), "No download task.", Toast.LENGTH_SHORT).show(); 115 | return; 116 | } 117 | 118 | for (DownloadInfo downloadInfo : downloadAdapter.getData()) { 119 | downloadManager.remove(downloadInfo); 120 | } 121 | 122 | downloadAdapter.clearData(); 123 | } 124 | 125 | //暂停所有,开始所有 126 | private void onPauseAllClick() { 127 | //无数据,可以按照需求来处理 128 | if (downloadAdapter.getData().size() == 0) { 129 | Toast.makeText(getActivity(), "No download task.", Toast.LENGTH_SHORT).show(); 130 | return; 131 | } 132 | 133 | pauseOrResumeAll(); 134 | } 135 | 136 | private void pauseOrResumeAll() { 137 | if (hasDownloading) { 138 | pauseAll(); 139 | hasDownloading = false; 140 | } else { 141 | resumeAll(); 142 | hasDownloading = true; 143 | } 144 | 145 | setPauseOrResumeButtonStatus(); 146 | } 147 | 148 | private void setPauseOrResumeButtonStatus() { 149 | hasDownloading = false; 150 | for (DownloadInfo downloadInfo : downloadAdapter.getData()) { 151 | if (DownloadInfo.STATUS_DOWNLOADING == downloadInfo.getStatus()) { 152 | //如果有一个的状态是正在下载,按钮就是暂停所有 153 | hasDownloading = true; 154 | break; 155 | } 156 | } 157 | 158 | 159 | if (hasDownloading) { 160 | bt_control_all.setText("Pause all"); 161 | } else { 162 | bt_control_all.setText("Start all"); 163 | } 164 | } 165 | 166 | private void resumeAll() { 167 | downloadManager.resumeAll(); 168 | reloadData(); 169 | } 170 | 171 | private void pauseAll() { 172 | downloadManager.pauseAll(); 173 | reloadData(); 174 | } 175 | 176 | private void reloadData() { 177 | downloadAdapter.notifyDataSetChanged(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /app/src/main/java/com/ixuea/android/downloader/simple/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.ixuea.android.downloader.simple.util; 2 | 3 | 4 | /** 5 | * Created by ixuea(http://a.ixuea.com/3) on 19/9/2021. 6 | */ 7 | 8 | public class FileUtil { 9 | public static String formatFileSize(long size) { 10 | String sFileSize; 11 | if (size > 0) { 12 | double dFileSize = (double) size; 13 | 14 | double kiloByte = dFileSize / 1024; 15 | if (kiloByte < 1 && kiloByte > 0) { 16 | return size + "Byte"; 17 | } 18 | double megaByte = kiloByte / 1024; 19 | if (megaByte < 1) { 20 | sFileSize = String.format("%.2f", kiloByte); 21 | return sFileSize + "K"; 22 | } 23 | 24 | double gigaByte = megaByte / 1024; 25 | if (gigaByte < 1) { 26 | sFileSize = String.format("%.2f", megaByte); 27 | return sFileSize + "M"; 28 | } 29 | 30 | double teraByte = gigaByte / 1024; 31 | if (teraByte < 1) { 32 | sFileSize = String.format("%.2f", gigaByte); 33 | return sFileSize + "G"; 34 | } 35 | 36 | sFileSize = String.format("%.2f", teraByte); 37 | return sFileSize + "T"; 38 | } 39 | return "0K"; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_download_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_download_manager.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |