├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── p1mankar │ │ └── filedownloader │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── p1mankar │ │ │ └── filedownloader │ │ │ ├── MainActivity.kt │ │ │ └── MyApplication.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.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-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── p1mankar │ └── filedownloader │ └── ExampleUnitTest.kt ├── build.gradle ├── contentDownloader ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── p1mankar │ │ └── contentdownloader │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── p1mankar │ │ └── contentdownloader │ │ └── downloadModule │ │ ├── http │ │ ├── DefaultHttpClient.kt │ │ └── HttpClient.kt │ │ ├── internal │ │ ├── DownloadDispatcher.kt │ │ ├── DownloadRequest.kt │ │ ├── DownloadRequestQueue.kt │ │ └── DownloadTask.kt │ │ └── utils │ │ ├── Constanst.kt │ │ ├── Downloader.kt │ │ ├── DownloaderConfig.kt │ │ ├── FileDownloadOutputStream.kt │ │ ├── FileDownloadRandomAccessFile.kt │ │ ├── Status.kt │ │ └── Utils.kt │ └── test │ └── java │ └── com │ └── p1mankar │ └── contentdownloader │ └── ExampleUnitTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # File Downloader 2 | ### A file downloader library for Android with pause and resume support. 3 | 4 | 5 | ### Overview of library 6 | * This library can be used to download any type of file like images, video, pdf, APK etc. 7 | * Supports large file download. 8 | * Supports proper request canceling. 9 | * We can check the status of downloading with the given download ID. 10 | * Many requests can be made in parallel. 11 | * This file downloader library supports pause and resume while downloading a file. 12 | * This downloader library has a simple interface to make download requests. 13 | * This library gives callbacks for everything like onProgress, onCancel, onStart, onError, etc while downloading a file. 14 | * All types of customization are possible. 15 | 16 | 17 | 18 | ## Using Library in your Android application 19 | 20 | Update your settings.gradle file with the following dependency. 21 | 22 | ```groovy 23 | dependencyResolutionManagement { 24 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 25 | repositories { 26 | google() 27 | mavenCentral() 28 | maven { url 'https://jitpack.io' } // this one 29 | } 30 | } 31 | ``` 32 | 33 | Update your module level build.gradle file with the following dependency. 34 | 35 | ```groovy 36 | dependencies { 37 | implementation 'com.github.pavanmankar:FileDownloader:1.0.0' 38 | } 39 | ``` 40 | Do not forget to add internet permission in manifest if already not present 41 | 42 | ``` 43 | 44 | ``` 45 | 46 | ### Download Request 47 | ``` 48 | val request = downloader.newReqBuilder( 49 | url, 50 | dirPath, 51 | fileName, 52 | ).tag(TAG).build() 53 | 54 | downloadId = downloader.enqueue(request, 55 | onStart = { 56 | }, 57 | onProgress = { 58 | }, 59 | onPause = { 60 | }, 61 | onCompleted = { 62 | }, 63 | onError = { 64 | } 65 | ) 66 | ``` 67 | 68 | ### Pause a download request : 69 | 70 | ```kotlin 71 | downloader.pause(downloadId); 72 | ``` 73 | ### Resume a download request 74 | ```kotlin 75 | downloader.resume(downloadId); 76 | ``` 77 | 78 | ### Cancel a download request 79 | ```kotlin 80 | // Cancel with the download id 81 | downloader.cancel(downloadId); 82 | 83 | //Cancel by using tag 84 | downloader.cancel(TAG); 85 | 86 | // Cancel all the requests 87 | downloader.cancelAll(); 88 | ``` 89 | 90 | ### License 91 | ``` 92 | Copyright (C) 2024 Pavan Mankar 93 | 94 | Licensed under the Apache License, Version 2.0 (the "License"); 95 | you may not use this file except in compliance with the License. 96 | You may obtain a copy of the License at 97 | 98 | http://www.apache.org/licenses/LICENSE-2.0 99 | 100 | Unless required by applicable law or agreed to in writing, software 101 | distributed under the License is distributed on an "AS IS" BASIS, 102 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 103 | See the License for the specific language governing permissions and 104 | limitations under the License. 105 | ``` 106 | 107 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.p1mankar.filedownloader' 8 | compileSdk 34 9 | 10 | defaultConfig { 11 | applicationId "com.p1mankar.filedownloader" 12 | minSdk 21 13 | targetSdk 34 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | buildFeatures { 34 | viewBinding true 35 | } 36 | } 37 | 38 | dependencies { 39 | 40 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1") 41 | implementation 'androidx.core:core-ktx:1.7.0' 42 | implementation 'androidx.appcompat:appcompat:1.5.1' 43 | implementation 'com.google.android.material:material:1.7.0' 44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 45 | implementation project(path: ':contentDownloader') 46 | testImplementation 'junit:junit:4.13.2' 47 | androidTestImplementation 'androidx.test.ext:junit:1.1.4' 48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' 49 | } -------------------------------------------------------------------------------- /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/p1mankar/filedownloader/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.p1mankar.filedownloader 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.p1mankar.filedownloader", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/p1mankar/filedownloader/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.p1mankar.filedownloader 2 | 3 | import android.os.Bundle 4 | import android.os.Environment 5 | import android.util.Log 6 | import android.view.View 7 | import androidx.appcompat.app.AppCompatActivity 8 | import com.library.Downloader 9 | import com.p1mankar.filedownloader.databinding.ActivityMainBinding 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | companion object { 14 | private const val TAG = "MainActivity" 15 | } 16 | 17 | private lateinit var binding: ActivityMainBinding 18 | 19 | lateinit var dirPath: String 20 | lateinit var downloader: Downloader 21 | 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | binding = ActivityMainBinding.inflate(layoutInflater) 25 | setContentView(binding.root) 26 | 27 | downloader = (application as MyApplication).downloader 28 | dirPath = Environment.getExternalStorageDirectory().path + "/Download" 29 | setOnClickListener() 30 | } 31 | 32 | private fun setOnClickListener() { 33 | binding.startCancelButton1.setOnClickListener { 34 | var downloadId1 = 0 35 | if (binding.startCancelButton1.text.equals("Start")) { 36 | val request = downloader.newReqBuilder( 37 | "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_30mb.mp4", 38 | dirPath, 39 | "bunny.mp4", 40 | ).tag(TAG + "1").build() 41 | downloadId1 = downloader.enqueue(request, onStart = { 42 | binding.status1.text = "Started" 43 | binding.startCancelButton1.text = "Cancel" 44 | Log.d(TAG, "On Start") 45 | }, onProgress = { 46 | binding.status1.text = "In Progress" 47 | binding.progressBar1.progress = it 48 | binding.progressText1.text = "$it%" 49 | Log.d(TAG, it.toString()) 50 | }, onPause = { 51 | binding.status1.text = "Paused" 52 | Log.d(TAG, "On Pause") 53 | }, onCompleted = { 54 | binding.status1.text = "Completed" 55 | Log.d(TAG, "On Complete") 56 | binding.startCancelButton1.text = "Completed" 57 | }, onError = { 58 | binding.status1.text = "Error : $it" 59 | binding.resumePauseButton1.visibility = View.GONE 60 | binding.progressBar1.progress = 0 61 | binding.progressText1.text = "0%" 62 | Log.d(TAG, it) 63 | }) 64 | } else if (binding.startCancelButton1.text.equals("Cancel")) { 65 | downloader.cancel(TAG + "1") 66 | binding.startCancelButton1.text = "Start" 67 | } 68 | 69 | } 70 | 71 | binding.startCancelButton2.setOnClickListener { 72 | var downloadId2 = 0 73 | if (binding.startCancelButton2.text.equals("Start")) { 74 | val request1 = downloader.newReqBuilder( 75 | "https://media.giphy.com/media/Bk0CW5frw4qfS/giphy.gif", 76 | dirPath, 77 | "docu.gif", 78 | ).tag(TAG + "2").build() 79 | downloadId2 = downloader.enqueue(request1, onStart = { 80 | binding.status2.text = "Started" 81 | binding.startCancelButton2.text = "Cancel" 82 | Log.d(TAG, "On Start") 83 | }, onProgress = { 84 | binding.status2.text = "In Progress" 85 | binding.progressBar2.progress = it 86 | binding.progressText2.text = "$it%" 87 | Log.d(TAG, it.toString()) 88 | }, onPause = { 89 | binding.status2.text = "Paused" 90 | Log.d(TAG, "On Pause") 91 | }, onCompleted = { 92 | binding.status2.text = "Completed" 93 | Log.d(TAG, "On Complete") 94 | binding.startCancelButton2.text = "Completed" 95 | }, onError = { 96 | binding.status2.text = "Error : $it" 97 | binding.resumePauseButton2.visibility = View.GONE 98 | binding.progressBar2.progress = 0 99 | binding.progressText2.text = "0%" 100 | Log.d(TAG, it) 101 | }) 102 | } else if (binding.startCancelButton2.text.equals("Cancel")) { 103 | downloader.cancel(downloadId2) 104 | binding.startCancelButton2.text = "Start" 105 | } 106 | 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/main/java/com/p1mankar/filedownloader/MyApplication.kt: -------------------------------------------------------------------------------- 1 | package com.p1mankar.filedownloader 2 | 3 | import android.app.Application 4 | import com.library.Downloader 5 | 6 | class MyApplication : Application() { 7 | 8 | lateinit var downloader: Downloader 9 | 10 | override fun onCreate() { 11 | super.onCreate() 12 | downloader = Downloader.create() 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /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_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 14 | 15 | 20 | 21 | 30 | 31 | 40 | 41 | 50 | 51 | 61 | 62 |