@kesha-antonov/react-native-background-downloader 3.2.6 → 4.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +347 -44
  2. package/android/build.gradle +48 -5
  3. package/android/src/main/java/com/eko/Downloader.kt +133 -0
  4. package/android/src/main/java/com/eko/RNBGDTaskConfig.kt +12 -0
  5. package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +863 -0
  6. package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.kt +39 -0
  7. package/android/src/main/java/com/eko/handlers/OnBegin.kt +92 -0
  8. package/android/src/main/java/com/eko/handlers/OnBeginState.kt +10 -0
  9. package/android/src/main/java/com/eko/handlers/OnProgress.kt +121 -0
  10. package/android/src/main/java/com/eko/handlers/OnProgressState.kt +9 -0
  11. package/android/src/main/java/com/eko/interfaces/BeginCallback.kt +7 -0
  12. package/android/src/main/java/com/eko/interfaces/ProgressCallback.kt +5 -0
  13. package/android/src/main/java/com/eko/utils/FileUtils.kt +62 -0
  14. package/android/src/newarch/java/com/eko/RNBackgroundDownloaderModule.kt +60 -0
  15. package/android/src/oldarch/java/com/eko/RNBackgroundDownloaderModule.kt +70 -0
  16. package/app.plugin.js +3 -0
  17. package/example/android/app/debug.keystore +0 -0
  18. package/ios/.DS_Store +0 -0
  19. package/ios/RNBGDTaskConfig.h +13 -54
  20. package/ios/RNBGDTaskConfig.mm +62 -0
  21. package/ios/RNBackgroundDownloader.h +15 -2
  22. package/ios/RNBackgroundDownloader.mm +973 -0
  23. package/ios/RNBackgroundDownloader.xcodeproj/project.pbxproj +13 -21
  24. package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcuserdata/kesha.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  25. package/package.json +52 -32
  26. package/plugin/build/index.d.ts +3 -0
  27. package/plugin/build/index.js +169 -0
  28. package/react-native-background-downloader.podspec +22 -8
  29. package/src/DownloadTask.ts +184 -0
  30. package/src/NativeRNBackgroundDownloader.ts +83 -0
  31. package/src/index.ts +302 -0
  32. package/src/types.ts +124 -0
  33. package/android/src/main/java/com/eko/Downloader.java +0 -148
  34. package/android/src/main/java/com/eko/RNBGDTaskConfig.java +0 -21
  35. package/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java +0 -562
  36. package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.java +0 -22
  37. package/android/src/main/java/com/eko/handlers/OnBegin.java +0 -88
  38. package/android/src/main/java/com/eko/handlers/OnBeginState.java +0 -16
  39. package/android/src/main/java/com/eko/handlers/OnProgress.java +0 -123
  40. package/android/src/main/java/com/eko/handlers/OnProgressState.java +0 -14
  41. package/android/src/main/java/com/eko/interfaces/BeginCallback.java +0 -7
  42. package/android/src/main/java/com/eko/interfaces/ProgressCallback.java +0 -5
  43. package/android/src/main/java/com/eko/utils/FileUtils.java +0 -67
  44. package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/.ci/compile +0 -351
  45. package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/testsuite/libffi.bhaible/Makefile +0 -28
  46. package/index.d.ts +0 -142
  47. package/index.ts +0 -172
  48. package/ios/RNBackgroundDownloader.m +0 -630
  49. package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  50. package/lib/DownloadTask.ts +0 -116
package/README.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader.svg)](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader)
4
4
 
5
+ ## 🚧👷 Important notice
6
+
7
+ There's currently WIP going on to make the library support New Architecture. If you have any issues, please report them. If you want to contribute, please do so.
8
+
9
+ The most stable version is `3.2.6`. If you want to use the latest version, please be aware that it's a work in progress.
10
+
11
+ Readme for that version: [3.2.6 readme](https://github.com/kesha-antonov/react-native-background-downloader/blob/8f4b8a844a2d7f00d1558f6ea65bac94c8dd6fc9/README.md)
12
+
13
+ I'm working on making the library compatible with the New Architecture while keeping backward compatibility with the old one. I plan to use Nitro Modules so apps on the old architecture can also benefit from the performance improvements.
14
+
5
15
  # @kesha-antonov/react-native-background-downloader
6
16
 
7
17
  A library for React-Native to help you download large files on iOS and Android both in the foreground and most importantly in the background.
@@ -41,6 +51,44 @@ Then:
41
51
  cd ios && pod install
42
52
  ```
43
53
 
54
+ ### New Architecture Support
55
+
56
+ This library supports React Native's New Architecture (Fabric + TurboModules) starting from React Native 0.70+.
57
+
58
+ #### Automatic Detection
59
+ The library automatically detects whether the New Architecture is enabled in your app and uses the appropriate implementation:
60
+ - **New Architecture**: Uses TurboModules for optimal performance
61
+ - **Legacy Architecture**: Uses the traditional bridge implementation
62
+
63
+ #### Manual Setup (Advanced)
64
+ If you need to manually configure the package for New Architecture:
65
+
66
+ **iOS**: The library automatically detects New Architecture via compile-time flags.
67
+
68
+ **Android**: For New Architecture, you can optionally use `RNBackgroundDownloaderTurboPackage` instead of the default package:
69
+ ```java
70
+ import com.eko.RNBackgroundDownloaderTurboPackage;
71
+
72
+ // In your MainApplication.java
73
+ @Override
74
+ protected List<ReactPackage> getPackages() {
75
+ return Arrays.<ReactPackage>asList(
76
+ // ... other packages
77
+ new RNBackgroundDownloaderTurboPackage() // For New Architecture
78
+ );
79
+ }
80
+ ```
81
+
82
+ #### Known Issues with New Architecture
83
+ When using larger files with the New Architecture, you may encounter `ERROR_CANNOT_RESUME` (error code 1008). This is a known limitation of Android's DownloadManager, not specific to this library or the New Architecture. The error includes enhanced messaging to help diagnose the issue.
84
+
85
+ **Workaround:** If you encounter this error frequently with large files, consider:
86
+ 1. Breaking large downloads into smaller chunks
87
+ 2. Implementing retry logic in your app
88
+ 3. Using alternative download strategies for very large files
89
+
90
+ The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
91
+
44
92
  ### Mostly automatic installation
45
93
  Any React Native version **`>= 0.60`** supports autolinking so nothing should be done.
46
94
 
@@ -76,21 +124,99 @@ For anything **`< 0.60`** run the following link command
76
124
  </details>
77
125
 
78
126
  ### iOS - Extra Mandatory Step
79
- In your `AppDelegate.m` add the following code:
80
- ```objc
81
- ...
82
- #import <RNBackgroundDownloader.h>
83
127
 
84
- ...
128
+ #### Option 1: Using Expo Config Plugin (Recommended for Expo/EAS users)
129
+
130
+ If you're using Expo or EAS Build, you can use the included Expo config plugin to automatically configure the iOS native code:
85
131
 
86
- - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
132
+ **In your `app.json`:**
133
+ ```json
87
134
  {
88
- [RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
135
+ "expo": {
136
+ "name": "Your App",
137
+ "plugins": [
138
+ "@kesha-antonov/react-native-background-downloader"
139
+ ]
140
+ }
89
141
  }
142
+ ```
90
143
 
91
- ...
144
+ **Or in your `app.config.js`:**
145
+ ```js
146
+ export default {
147
+ expo: {
148
+ name: "Your App",
149
+ plugins: [
150
+ "@kesha-antonov/react-native-background-downloader"
151
+ ]
152
+ }
153
+ }
92
154
  ```
93
- Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
155
+
156
+ The plugin will automatically:
157
+ - Add the required import to your AppDelegate (Objective-C) or Bridging Header (Swift)
158
+ - Add the `handleEventsForBackgroundURLSession` method to your AppDelegate
159
+ - Handle both React Native < 0.77 (Objective-C) and >= 0.77 (Swift) projects
160
+
161
+ After adding the plugin, run:
162
+ ```bash
163
+ expo prebuild --clean
164
+ ```
165
+
166
+ #### Option 2: Manual Setup
167
+
168
+ <details>
169
+ <summary>Manual setup for React Native 0.77+ (Click to expand)</summary>
170
+
171
+ In your project bridging header file (e.g. `ios/{projectName}-Bridging-Header.h`)
172
+ add an import for RNBackgroundDownloader:
173
+
174
+ ```objc
175
+ ...
176
+ #import <RNBackgroundDownloader.h>
177
+ ```
178
+
179
+ Then in your `AppDelegate.swift` add the following method inside of your `AppDelegate` class:
180
+
181
+ ```swift
182
+ ...
183
+
184
+ @main
185
+ class AppDelegate: UIResponder, UIApplicationDelegate
186
+ ...
187
+
188
+ func application(
189
+ _ application: UIApplication,
190
+ handleEventsForBackgroundURLSession identifier: String,
191
+ completionHandler: @escaping () -> Void
192
+ ) {
193
+ RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
194
+ }
195
+ }
196
+ ...
197
+ ```
198
+ Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
199
+ </details>
200
+
201
+ <details>
202
+ <summary>Manual setup for React Native < 0.77 (Click to expand)</summary>
203
+
204
+ In your `AppDelegate.m` add the following code:
205
+ ```objc
206
+ ...
207
+ #import <RNBackgroundDownloader.h>
208
+
209
+ ...
210
+
211
+ - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
212
+ {
213
+ [RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
214
+ }
215
+
216
+ ...
217
+ ```
218
+ Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
219
+ </details>
94
220
 
95
221
  ## Usage
96
222
 
@@ -98,11 +224,11 @@ Failing to add this code will result in canceled background downloads. If Xcode
98
224
 
99
225
  ```javascript
100
226
  import { Platform } from 'react-native'
101
- import { download, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
227
+ import { createDownloadTask, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
102
228
 
103
229
  const jobId = 'file123'
104
230
 
105
- let task = download({
231
+ let task = createDownloadTask({
106
232
  id: jobId,
107
233
  url: 'https://link-to-very.large/file.zip',
108
234
  destination: `${directories.documents}/file.zip`,
@@ -122,16 +248,64 @@ let task = download({
122
248
  console.log('Download canceled due to error: ', { error, errorCode });
123
249
  })
124
250
 
251
+ // starts download
252
+ task.start()
253
+
125
254
  // Pause the task (iOS only)
255
+ // Note: On Android, pause/resume is not supported by DownloadManager
126
256
  task.pause()
127
257
 
128
258
  // Resume after pause (iOS only)
259
+ // Note: On Android, pause/resume is not supported by DownloadManager
129
260
  task.resume()
130
261
 
131
262
  // Cancel the task
132
263
  task.stop()
133
264
  ```
134
265
 
266
+ ### Platform-Aware Pause/Resume
267
+
268
+ ```javascript
269
+ import { Platform } from 'react-native'
270
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
271
+
272
+ const task = createDownloadTask({
273
+ id: 'file123',
274
+ url: 'https://link-to-very.large/file.zip',
275
+ destination: `${directories.documents}/file.zip`,
276
+ metadata: {}
277
+ }).begin(({ expectedBytes, headers }) => {
278
+ console.log(`Going to download ${expectedBytes} bytes!`)
279
+ }).progress(({ bytesDownloaded, bytesTotal }) => {
280
+ console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
281
+ }).done(({ bytesDownloaded, bytesTotal }) => {
282
+ console.log('Download is done!', { bytesDownloaded, bytesTotal })
283
+ }).error(({ error, errorCode }) => {
284
+ console.log('Download canceled due to error: ', { error, errorCode });
285
+ })
286
+
287
+ task.start()
288
+
289
+ // Platform-aware pause/resume handling
290
+ function pauseDownloadTask() {
291
+ if (Platform.OS === 'ios') {
292
+ task.pause()
293
+ console.log('Download paused')
294
+ } else {
295
+ console.log('Pause not supported on Android. Consider using stop() instead.')
296
+ }
297
+ }
298
+
299
+ function resumeDownloadTask() {
300
+ if (Platform.OS === 'ios') {
301
+ task.resume()
302
+ console.log('Download resumed')
303
+ } else {
304
+ console.log('Resume not supported on Android. You may need to restart the download.')
305
+ }
306
+ }
307
+ ```
308
+
135
309
  ### Re-Attaching to background downloads
136
310
 
137
311
  This is the main selling point of this library (but it's free!).
@@ -141,9 +315,9 @@ What happens to your downloads after the OS stopped your app? Well, they are sti
141
315
  Add this code to app's init stage, and you'll never lose a download again!
142
316
 
143
317
  ```javascript
144
- import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downloader'
318
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
145
319
 
146
- let lostTasks = await RNBackgroundDownloader.checkForExistingDownloads()
320
+ let lostTasks = await getExistingDownloadTasks()
147
321
  for (let task of lostTasks) {
148
322
  console.log(`Task ${task.id} was found!`)
149
323
  task.progress(({ bytesDownloaded, bytesTotal }) => {
@@ -161,9 +335,11 @@ for (let task of lostTasks) {
161
335
  ### Using custom headers
162
336
  If you need to send custom headers with your download request, you can do in it 2 ways:
163
337
 
164
- 1) Globally using `RNBackgroundDownloader.setConfig()`:
338
+ 1) Globally using `setConfig()`:
165
339
  ```javascript
166
- RNBackgroundDownloader.setConfig({
340
+ import { setConfig } from '@kesha-antonov/react-native-background-downloader'
341
+
342
+ setConfig({
167
343
  headers: {
168
344
  Authorization: 'Bearer 2we$@$@Ddd223',
169
345
  }
@@ -171,12 +347,14 @@ RNBackgroundDownloader.setConfig({
171
347
  ```
172
348
  This way, all downloads with have the given headers.
173
349
 
174
- 2) Per download by passing a headers object in the options of `RNBackgroundDownloader.download()`:
350
+ 2) Per download by passing a headers object in the options of `createDownloadTask()`:
175
351
  ```javascript
176
- let task = RNBackgroundDownloader.download({
352
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
353
+
354
+ const task = createDownloadTask({
177
355
  id: 'file123',
178
356
  url: 'https://link-to-very.large/file.zip'
179
- destination: `${RNBackgroundDownloader.directories.documents}/file.zip`,
357
+ destination: `${directories.documents}/file.zip`,
180
358
  headers: {
181
359
  Authorization: 'Bearer 2we$@$@Ddd223'
182
360
  }
@@ -189,14 +367,93 @@ let task = RNBackgroundDownloader.download({
189
367
  }).error(({ error, errorCode }) => {
190
368
  console.log('Download canceled due to error: ', { error, errorCode })
191
369
  })
370
+
371
+ task.start()
372
+ ```
373
+ Headers given in `createDownloadTask()` are **merged** with the ones given in `setConfig({ headers: { ... } })`.
374
+
375
+ ### Handling Slow-Responding URLs
376
+
377
+ This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:
378
+
379
+ - `Connection: keep-alive` - Keeps the connection open for better handling
380
+ - `Keep-Alive: timeout=600, max=1000` - Sets a 10-minute keep-alive timeout
381
+ - `User-Agent: ReactNative-BackgroundDownloader/3.2.6` - Proper user agent for better server compatibility
382
+
383
+ These headers help prevent downloads from getting stuck in "pending" state when servers take several minutes to respond initially. You can override these headers by providing your own in the `headers` option.
384
+
385
+ ### Handling URLs with Many Redirects (Android)
386
+
387
+ Android's DownloadManager has a built-in redirect limit that can cause `ERROR_TOO_MANY_REDIRECTS` for URLs with multiple redirects (common with podcast URLs, tracking services, CDNs, etc.).
388
+
389
+ To handle this, you can use the `maxRedirects` option to pre-resolve redirects before passing the final URL to DownloadManager:
390
+
391
+ ```javascript
392
+ import { Platform } from 'react-native'
393
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
394
+
395
+ // Example: Podcast URL with multiple redirects
396
+ const task = createDownloadTask({
397
+ id: 'podcast-episode',
398
+ url: 'https://pdst.fm/e/chrt.fm/track/479722/arttrk.com/p/example.mp3',
399
+ destination: `${directories.documents}/episode.mp3`,
400
+ maxRedirects: 10, // Follow up to 10 redirects before downloading
401
+ }).begin(({ expectedBytes }) => {
402
+ console.log(`Going to download ${expectedBytes} bytes!`)
403
+ }).progress(({ bytesDownloaded, bytesTotal }) => {
404
+ console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
405
+ }).done(({ bytesDownloaded, bytesTotal }) => {
406
+ console.log('Download is done!', { bytesDownloaded, bytesTotal })
407
+ }).error(({ error, errorCode }) => {
408
+ console.log('Download canceled due to error: ', { error, errorCode })
409
+
410
+ if (errorCode === 1005) { // ERROR_TOO_MANY_REDIRECTS
411
+ console.log('Consider increasing maxRedirects or using a different URL')
412
+ }
413
+ })
414
+
415
+ task.start()
192
416
  ```
193
- Headers given in the `download` function are **merged** with the ones given in `setConfig({ headers: { ... } })`.
417
+
418
+ **Notes on maxRedirects:**
419
+ - Only available on Android (iOS handles redirects automatically)
420
+ - If not specified or set to 0, no redirect resolution is performed
421
+ - Uses HEAD requests to resolve redirects efficiently
422
+ - Falls back to original URL if redirect resolution fails
423
+ - Respects the same headers and timeouts as the main download
194
424
 
195
425
  ## API
196
426
 
197
- ### RNBackgroundDownloader
427
+ ### Named Exports
428
+
429
+ The library exports the following functions and objects:
430
+
431
+ ```typescript
432
+ import {
433
+ setConfig,
434
+ createDownloadTask,
435
+ getExistingDownloadTasks,
436
+ ensureDownloadsAreRunning,
437
+ completeHandler,
438
+ directories
439
+ } from '@kesha-antonov/react-native-background-downloader'
440
+ ```
441
+
442
+ **Default Export:**
443
+
444
+ ```typescript
445
+ import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downloader'
198
446
 
199
- ### `download(options)`
447
+ // Contains all the above as properties:
448
+ RNBackgroundDownloader.setConfig
449
+ RNBackgroundDownloader.createDownloadTask
450
+ RNBackgroundDownloader.getExistingDownloadTasks
451
+ RNBackgroundDownloader.ensureDownloadsAreRunning
452
+ RNBackgroundDownloader.completeHandler
453
+ RNBackgroundDownloader.directories
454
+ ```
455
+
456
+ ### `createDownloadTask(options)`
200
457
 
201
458
  Download a file to destination
202
459
 
@@ -206,11 +463,12 @@ An object containing options properties
206
463
 
207
464
  | Property | Type | Required | Platforms | Info |
208
465
  | ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
209
- | `id` | String | ✅ | All | A Unique ID to provide for this download. This ID will help to identify the download task when the app re-launches |
466
+ | `id` | String | ✅ | All | A unique ID to provide for this download. This ID will help to identify the download task when the app re-launches |
210
467
  | `url` | String | ✅ | All | URL to file you want to download |
211
- | `destination` | String | ✅ | All | Where to copy the file to once the download is done |
212
- | `metadata` | Object | | All | Data to be preserved on reboot. |
213
- | `headers` | Object | | All | Costume headers to add to the download request. These are merged with the headers given in the `setConfig({ headers: { ... } })` function |
468
+ | `destination` | String | ✅ | All | Where to copy the file to once the download is done. The 'file://' prefix will be automatically removed if present |
469
+ | `metadata` | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON |
470
+ | `headers` | Record<string, string \| null> | | All | Custom headers to add to the download request. These are merged with the headers given in `setConfig({ headers: { ... } })`. Headers with null values will be removed |
471
+ | `maxRedirects` | Number | | Android | Maximum number of redirects to follow before passing URL to DownloadManager. If not specified or 0, no redirect resolution is performed. Helps avoid ERROR_TOO_MANY_REDIRECTS for URLs with many redirects (e.g., podcast URLs) |
214
472
  | `isAllowedOverRoaming` | Boolean | | Android | whether this download may proceed over a roaming connection. By default, roaming is allowed |
215
473
  | `isAllowedOverMetered` | Boolean | | Android | Whether this download may proceed over a metered network connection. By default, metered networks are allowed |
216
474
  | `isNotificationVisible` | Boolean | | Android | Whether to show a download notification or not |
@@ -218,47 +476,65 @@ An object containing options properties
218
476
 
219
477
  **returns**
220
478
 
221
- `DownloadTask` - The download task to control and monitor this download
479
+ `DownloadTask` - The download task to control and monitor this download. Call `task.start()` to begin the download.
222
480
 
223
- ### `checkForExistingDownloads()`
481
+ ### `getExistingDownloadTasks()`
224
482
 
225
- Checks for downloads that ran in background while you app was terminated. And also forces them to resume downloads.
483
+ Checks for downloads that ran in background while your app was terminated.
226
484
 
227
485
  Recommended to run at the init stage of the app.
228
486
 
229
487
  **returns**
230
488
 
231
- `DownloadTask[]` - Array of tasks that were running in the background so you can re-attach callbacks to them
489
+ `Promise<DownloadTask[]>` - A promise that resolves to an array of tasks that were running in the background so you can re-attach callbacks to them
490
+
491
+ ### `setConfig(config)`
492
+
493
+ Sets global configuration for the downloader.
232
494
 
233
- ### `setConfig({})`
495
+ **config**
496
+
497
+ An object containing configuration properties
234
498
 
235
499
  | Name | Type | Info |
236
500
  | -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
237
- | `headers` | Object | optional headers to use in all future downloads |
238
- | `progressInterval` | Number | Interval in which download progress sent from downloader. Number should be >= 250. It's in ms |
239
- | `isLogsEnabled` | Boolean | Enables/disables logs in library |
501
+ | `headers` | Record<string, string \| null> | Optional headers to use in all future downloads. Headers with null values will be removed |
502
+ | `progressInterval` | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 |
503
+ | `progressMinBytes` | Number | Minimum number of bytes that must be downloaded before a progress event is emitted. When set to 0, only the percentage threshold (1% change) triggers progress updates. Default is 1048576 (1MB) |
504
+ | `isLogsEnabled` | Boolean | Enables/disables debug logs in library. Default is false |
240
505
 
241
506
  ### DownloadTask
242
507
 
243
- A class representing a download task created by `RNBackgroundDownloader.download`
508
+ A class representing a download task created by `createDownloadTask()`. Note: You must call `task.start()` to begin the download after setting up event handlers.
244
509
 
245
510
  ### `Members`
246
511
  | Name | Type | Info |
247
512
  | -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
248
- | `id` | String | The id you gave the task when calling `RNBackgroundDownloader.download` |
249
- | `metadata` | Object | The metadata you gave the task when calling `RNBackgroundDownloader.download` |
513
+ | `id` | String | The id you gave the task when calling `createDownloadTask` |
514
+ | `metadata` | Record<string, unknown> | The metadata you gave the task when calling `createDownloadTask` |
515
+ | `state` | 'PENDING' \| 'DOWNLOADING' \| 'PAUSED' \| 'DONE' \| 'FAILED' \| 'STOPPED' | Current state of the download task |
250
516
  | `bytesDownloaded` | Number | The number of bytes currently written by the task |
251
517
  | `bytesTotal` | Number | The number bytes expected to be written by this task or more plainly, the file size being downloaded |
518
+ | `downloadParams` | DownloadParams | The download parameters set for this task |
252
519
 
253
- ### `completeHandler(jobId)`
520
+ ### `completeHandler(jobId: string)`
254
521
 
255
522
  Finishes download job and informs OS that app can be closed in background if needed.
256
523
  After finishing download in background you have some time to process your JS logic and finish the job.
257
524
 
258
- ### `ensureDownloadsAreRunning` (iOS only)
525
+ **Parameters:**
526
+ - `jobId` (String) - The ID of the download task to complete
527
+
528
+ **Note:** This should be called after processing your download in the `done` callback to properly signal completion to the OS.
529
+
530
+ ### `ensureDownloadsAreRunning()` (iOS only)
259
531
 
260
532
  Pauses and resumes all downloads - this is fix for stuck downloads. Use it when your app loaded and is ready for handling downloads (all your logic loaded and ready to handle download callbacks).
261
533
 
534
+ **returns**
535
+
536
+ `Promise<void>` - A promise that resolves when all downloads have been paused and resumed
537
+
262
538
  Here's example of how you can use it:
263
539
 
264
540
  1. When your app just loaded
@@ -266,7 +542,9 @@ Here's example of how you can use it:
266
542
  Either stop all tasks:
267
543
 
268
544
  ```javascript
269
- const tasks = await checkForExistingDownloads()
545
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
546
+
547
+ const tasks = await getExistingDownloadTasks()
270
548
  for (const task of tasks)
271
549
  task.stop()
272
550
  ```
@@ -274,7 +552,9 @@ for (const task of tasks)
274
552
  Or re-attach them:
275
553
 
276
554
  ```javascript
277
- const tasks = await checkForExistingDownloads()
555
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
556
+
557
+ const tasks = await getExistingDownloadTasks()
278
558
  for (const task of tasks) {
279
559
  task.pause()
280
560
  //
@@ -289,6 +569,7 @@ for (const task of tasks) {
289
569
  3. Add listener to handle when your app goes foreground (be sure to do it only after you stopped all tasks or re-attached them!)
290
570
 
291
571
  ```javascript
572
+ import { ensureDownloadsAreRunning } from '@kesha-antonov/react-native-background-downloader'
292
573
 
293
574
  function handleAppStateChange (appState) {
294
575
  if (appState !== 'active')
@@ -309,17 +590,21 @@ All callback methods return the current instance of the `DownloadTask` for chain
309
590
 
310
591
  | Function | Callback Arguments | Info|
311
592
  | ---------- | --------------------------------- | ---- |
312
- | `begin` | { expectedBytes, headers } | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
313
- | `progress` | { bytesDownloaded, bytesTotal } | Called at max every 1.5s so you can update your progress bar accordingly |
314
- | `done` | { bytesDownloaded, bytesTotal } | Called when the download is done, the file is at the destination you've set |
315
- | `error` | { error, errorCode } | Called when the download stops due to an error |
593
+ | `begin` | `{ expectedBytes: number, headers: Record<string, string \| null> }` | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
594
+ | `progress` | `{ bytesDownloaded: number, bytesTotal: number }` | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly |
595
+ | `done` | `{ bytesDownloaded: number, bytesTotal: number }` | Called when the download is done, the file is at the destination you've set |
596
+ | `error` | `{ error: string, errorCode: number }` | Called when the download stops due to an error |
316
597
 
317
598
  ### `pause()` (iOS only)
318
599
  Pauses the download
319
600
 
601
+ **Note:** This functionality is not supported on Android due to limitations in the DownloadManager API. On Android, calling this method will log a warning but will not crash the application.
602
+
320
603
  ### `resume()` (iOS only)
321
604
  Resumes a paused download
322
605
 
606
+ **Note:** This functionality is not supported on Android due to limitations in the DownloadManager API. On Android, calling this method will log a warning but will not crash the application.
607
+
323
608
  ### `stop()`
324
609
  Stops the download for good and removes the file that was written so far
325
610
 
@@ -331,6 +616,24 @@ Stops the download for good and removes the file that was written so far
331
616
 
332
617
  An absolute path to the app's documents directory. It is recommended that you use this path as the target of downloaded files.
333
618
 
619
+ ## Platform-Specific Limitations
620
+
621
+ ### Android DownloadManager Limitations
622
+
623
+ The Android implementation uses the system's `DownloadManager` service, which has some limitations compared to iOS:
624
+
625
+ #### Pause/Resume Not Supported
626
+ - **Issue**: Android's DownloadManager does not provide a public API for pausing and resuming downloads
627
+ - **Impact**: Calling `task.pause()` or `task.resume()` on Android will log a warning but not perform any action
628
+ - **Workaround**: If you need to stop a download, use `task.stop()` and restart it later with a new download request
629
+ - **Technical Details**: The private APIs needed for pause/resume functionality are not accessible to third-party applications
630
+
631
+ #### Alternative Approaches for Android
632
+ If pause/resume functionality is critical for your application, consider:
633
+ 1. Using `task.stop()` and tracking progress to restart downloads from where they left off (if the server supports range requests)
634
+ 2. Implementing a custom download solution for Android that doesn't use DownloadManager
635
+ 3. Designing your app flow to minimize the need for pause/resume functionality
636
+
334
637
  ## Rules for proguard-rules.pro
335
638
 
336
639
  In case of error `java.lang.IllegalStateException: TypeToken must be created with a type argument: new TypeToken<...>()` in Android release add this to `proguard-rules.pro`:
@@ -1,4 +1,9 @@
1
1
  apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+
4
+ def isNewArchitectureEnabled() {
5
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
6
+ }
2
7
 
3
8
  def safeExtGet(prop, fallback) {
4
9
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -11,19 +16,57 @@ android {
11
16
  namespace "com.eko"
12
17
  }
13
18
 
14
- compileSdkVersion safeExtGet('compileSdkVersion', 34)
19
+ compileSdkVersion safeExtGet('compileSdkVersion', 36)
15
20
 
16
21
  defaultConfig {
17
- minSdkVersion safeExtGet('minSdkVersion', 21)
18
- targetSdkVersion safeExtGet('targetSdkVersion', 34)
22
+ minSdkVersion safeExtGet('minSdkVersion', 24)
23
+ targetSdkVersion safeExtGet('targetSdkVersion', 36)
19
24
  versionCode 1
20
25
  versionName '1.0'
26
+
27
+ // Support for 16KB memory page sizes (Android 15+)
28
+ ndk {
29
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
30
+ }
31
+
32
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
33
+ }
34
+
35
+ buildFeatures {
36
+ buildConfig true
37
+ }
38
+
39
+ sourceSets {
40
+ main {
41
+ if (isNewArchitectureEnabled()) {
42
+ java.srcDirs += ['src/newarch/java']
43
+ } else {
44
+ java.srcDirs += ['src/oldarch/java']
45
+ }
46
+ }
47
+ }
48
+
49
+ // Configure packaging options for 16KB page size support
50
+ packagingOptions {
51
+ jniLibs {
52
+ useLegacyPackaging = false
53
+ }
21
54
  }
22
55
  }
23
56
 
57
+ repositories {
58
+ mavenCentral()
59
+ google()
60
+ }
61
+
24
62
  dependencies {
25
63
  //noinspection GradleDynamicVersion
26
64
  implementation 'com.facebook.react:react-native:+'
27
- implementation 'com.tencent:mmkv:1.3.12'
28
- implementation 'com.google.code.gson:gson:2.11.0'
65
+
66
+ // MMKV dependency updated for 16KB page size support
67
+ // MMKV 2.2.0+ includes support for 16KB memory page sizes required by Android 15+
68
+ // Uses mmkv-shared to maintain compatibility with react-native-mmkv 4+
69
+ implementation 'com.tencent:mmkv-shared:2.2.0'
70
+
71
+ implementation 'com.google.code.gson:gson:2.12.1'
29
72
  }