@kesha-antonov/react-native-background-downloader 4.3.4 → 4.3.7

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.
package/README.md CHANGED
@@ -355,6 +355,43 @@ task.start()
355
355
  ```
356
356
  Headers given in `createDownloadTask()` are **merged** with the ones given in `setConfig({ headers: { ... } })`.
357
357
 
358
+ ### Enabling Debug Logs
359
+
360
+ The library includes verbose debug logging that can help diagnose download issues. Logging is disabled by default but can be enabled at runtime using `setConfig()`. **Logging works in both debug and production/release builds.**
361
+
362
+ ```javascript
363
+ import { setConfig } from '@kesha-antonov/react-native-background-downloader'
364
+
365
+ // Option 1: Enable native console logging (logs appear in Xcode/Android Studio console)
366
+ setConfig({
367
+ isLogsEnabled: true
368
+ })
369
+
370
+ // Option 2: Enable logging with a JavaScript callback to capture logs in your app
371
+ setConfig({
372
+ isLogsEnabled: true,
373
+ logCallback: (log) => {
374
+ // log.message - The debug message
375
+ // log.taskId - Optional task ID associated with the log (iOS only)
376
+ console.log('[BackgroundDownloader]', log.message)
377
+
378
+ // You can also send logs to your analytics/crash reporting service
379
+ // crashlytics.log(log.message)
380
+ }
381
+ })
382
+
383
+ // Disable logging
384
+ setConfig({
385
+ isLogsEnabled: false
386
+ })
387
+ ```
388
+
389
+ **Notes:**
390
+ - When `isLogsEnabled` is `true`, native debug logs (NSLog on iOS, Log.d/w/e on Android) are printed
391
+ - The `logCallback` function is called for each native debug log (iOS only sends logs to callback currently)
392
+ - Logs include detailed information about download lifecycle, session management, and errors
393
+ - In production builds, logs are only printed when explicitly enabled via `isLogsEnabled`
394
+
358
395
  ### Handling Slow-Responding URLs
359
396
 
360
397
  This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:
@@ -469,7 +506,27 @@ An object containing configuration properties
469
506
  | `headers` | Record<string, string \| null> | Optional headers to use in all future downloads. Headers with null values will be removed |
470
507
  | `progressInterval` | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 |
471
508
  | `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) |
472
- | `isLogsEnabled` | Boolean | Enables/disables debug logs in library. Default is false |
509
+ | `isLogsEnabled` | Boolean | Enables/disables verbose debug logs in native code (iOS and Android). Works in both debug and release builds. Default is false |
510
+ | `logCallback` | (log: { message: string, taskId?: string }) => void | Optional callback function to receive native debug logs in JavaScript. Only called when `isLogsEnabled` is true |
511
+
512
+ **Example:**
513
+
514
+ ```javascript
515
+ import { setConfig } from '@kesha-antonov/react-native-background-downloader'
516
+
517
+ // Enable verbose logging with callback
518
+ setConfig({
519
+ isLogsEnabled: true,
520
+ logCallback: (log) => {
521
+ console.log('[BackgroundDownloader]', log.message, log.taskId ? `(${log.taskId})` : '')
522
+ }
523
+ })
524
+
525
+ // Or just enable native console logging without JS callback
526
+ setConfig({
527
+ isLogsEnabled: true
528
+ })
529
+ ```
473
530
 
474
531
  ### DownloadTask
475
532
 
@@ -40,7 +40,7 @@ object DownloadConstants {
40
40
  const val KEEP_ALIVE_HEADER_VALUE = "timeout=600, max=1000"
41
41
 
42
42
  /** Library version for User-Agent */
43
- const val VERSION = "4.3.4"
43
+ const val VERSION = "4.3.5"
44
44
 
45
45
  /** User-Agent string for HTTP requests */
46
46
  const val USER_AGENT = "ReactNative-BackgroundDownloader/$VERSION"
@@ -1,6 +1,5 @@
1
1
  package com.eko
2
2
 
3
- import android.util.Log
4
3
  import com.facebook.react.bridge.Arguments
5
4
  import com.facebook.react.bridge.WritableMap
6
5
  import com.facebook.react.modules.core.DeviceEventManagerModule
@@ -30,7 +29,7 @@ class DownloadEventEmitter(
30
29
  try {
31
30
  getEmitter().emit(eventName, params)
32
31
  } catch (e: Exception) {
33
- Log.w(TAG, "Failed to emit $eventName event: ${e.message}")
32
+ RNBackgroundDownloaderModuleImpl.logW(TAG, "Failed to emit $eventName event: ${e.message}")
34
33
  }
35
34
  }
36
35
 
@@ -48,7 +48,7 @@ class Downloader(private val context: Context) {
48
48
  val binder = service as ResumableDownloadService.LocalBinder
49
49
  downloadService = binder.getService()
50
50
  serviceBound = true
51
- Log.d(TAG, "ResumableDownloadService connected")
51
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "ResumableDownloadService connected")
52
52
 
53
53
  // Execute any pending operations
54
54
  var operation = pendingServiceOperations.poll()
@@ -61,7 +61,7 @@ class Downloader(private val context: Context) {
61
61
  override fun onServiceDisconnected(name: ComponentName?) {
62
62
  downloadService = null
63
63
  serviceBound = false
64
- Log.d(TAG, "ResumableDownloadService disconnected")
64
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "ResumableDownloadService disconnected")
65
65
  }
66
66
  }
67
67
 
@@ -132,7 +132,7 @@ class Downloader(private val context: Context) {
132
132
  // Remove any stale paused state from a previous download with the same ID
133
133
  val pausedInfo = pausedDownloads.remove(configId)
134
134
  if (pausedInfo != null) {
135
- Log.d(TAG, "Cleaned up stale paused state for: $configId")
135
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Cleaned up stale paused state for: $configId")
136
136
  TempFileUtils.deleteTempFile(pausedInfo.destination)
137
137
  }
138
138
  }
@@ -195,7 +195,7 @@ class Downloader(private val context: Context) {
195
195
  bytesTotal = bytesTotal
196
196
  )
197
197
 
198
- Log.d(TAG, "Paused download $configId at $bytesDownloaded/$bytesTotal bytes")
198
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Paused download $configId at $bytesDownloaded/$bytesTotal bytes")
199
199
  return true
200
200
  }
201
201
 
@@ -205,7 +205,7 @@ class Downloader(private val context: Context) {
205
205
  fun resume(configId: String, listener: ResumableDownloader.DownloadListener): Boolean {
206
206
  val pausedInfo = pausedDownloads[configId]
207
207
  if (pausedInfo == null) {
208
- Log.w(TAG, "No paused download found for configId: $configId")
208
+ RNBackgroundDownloaderModuleImpl.logW(TAG, "No paused download found for configId: $configId")
209
209
  return false
210
210
  }
211
211
 
@@ -220,7 +220,7 @@ class Downloader(private val context: Context) {
220
220
  listener
221
221
  )
222
222
 
223
- Log.d(TAG, "Resuming download $configId from ${pausedInfo.bytesDownloaded} bytes via service")
223
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Resuming download $configId from ${pausedInfo.bytesDownloaded} bytes via service")
224
224
  return true
225
225
  }
226
226
 
@@ -247,7 +247,7 @@ class Downloader(private val context: Context) {
247
247
  try {
248
248
  context.startForegroundService(startIntent)
249
249
  } catch (e: Exception) {
250
- Log.w(TAG, "Could not start foreground service: ${e.message}")
250
+ RNBackgroundDownloaderModuleImpl.logW(TAG, "Could not start foreground service: ${e.message}")
251
251
  }
252
252
  } else {
253
253
  context.startService(startIntent)
@@ -281,7 +281,7 @@ class Downloader(private val context: Context) {
281
281
  -1L, // Total bytes unknown
282
282
  listener
283
283
  )
284
- Log.d(TAG, "Started ResumableDownloader for $configId (DownloadManager fallback)")
284
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Started ResumableDownloader for $configId (DownloadManager fallback)")
285
285
  }
286
286
 
287
287
  /**
@@ -368,7 +368,7 @@ class Downloader(private val context: Context) {
368
368
  }
369
369
  }
370
370
  } catch (e: Exception) {
371
- Log.e(TAG, "Downloader: ${Log.getStackTraceString(e)}")
371
+ RNBackgroundDownloaderModuleImpl.logE(TAG, "Downloader: ${Log.getStackTraceString(e)}")
372
372
  }
373
373
 
374
374
  return result
@@ -45,6 +45,23 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
45
45
  )
46
46
 
47
47
  private val sharedLock = Any()
48
+
49
+ // Controls whether debug logs are enabled
50
+ @Volatile
51
+ private var isLogsEnabled = false
52
+
53
+ // Helper functions for conditional logging
54
+ fun logD(tag: String, message: String) {
55
+ if (isLogsEnabled) Log.d(tag, message)
56
+ }
57
+
58
+ fun logW(tag: String, message: String) {
59
+ if (isLogsEnabled) Log.w(tag, message)
60
+ }
61
+
62
+ fun logE(tag: String, message: String) {
63
+ if (isLogsEnabled) Log.e(tag, message)
64
+ }
48
65
  }
49
66
 
50
67
  // Storage manager for persistent state
@@ -91,7 +108,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
91
108
  ee = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
92
109
  }
93
110
  } catch (e: Exception) {
94
- Log.w(NAME, "Event emitter not ready yet: ${e.message}")
111
+ logW(NAME, "Event emitter not ready yet: ${e.message}")
95
112
  return null
96
113
  }
97
114
  }
@@ -107,9 +124,9 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
107
124
  if (!::ee.isInitialized) {
108
125
  try {
109
126
  ee = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
110
- Log.d(NAME, "Event emitter initialized eagerly before download")
127
+ logD(NAME, "Event emitter initialized eagerly before download")
111
128
  } catch (e: Exception) {
112
- Log.w(NAME, "Could not initialize event emitter: ${e.message}")
129
+ logW(NAME, "Could not initialize event emitter: ${e.message}")
113
130
  }
114
131
  }
115
132
  }
@@ -123,7 +140,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
123
140
  synchronized(sharedLock) {
124
141
  if (!isReceiverRegistered) {
125
142
  registerDownloadReceiver()
126
- Log.d(NAME, "Download receiver registered eagerly before download")
143
+ logD(NAME, "Download receiver registered eagerly before download")
127
144
  }
128
145
  }
129
146
  }
@@ -183,6 +200,10 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
183
200
  return constants
184
201
  }
185
202
 
203
+ fun setLogsEnabled(enabled: Boolean) {
204
+ isLogsEnabled = enabled
205
+ }
206
+
186
207
  fun initialize() {
187
208
  ee = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
188
209
  isInitialized = true
@@ -217,7 +238,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
217
238
  val cancelIntent = downloader.getCancelIntent(downloadId)
218
239
  if (cancelIntent != null) {
219
240
  downloader.clearCancelIntent(downloadId)
220
- Log.d(NAME, "Ignoring broadcast for ${cancelIntent.name.lowercase()} download: $downloadId")
241
+ logD(NAME, "Ignoring broadcast for ${cancelIntent.name.lowercase()} download: $downloadId")
221
242
  return
222
243
  }
223
244
 
@@ -284,7 +305,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
284
305
  try {
285
306
  reactContext.unregisterReceiver(it)
286
307
  } catch (e: Exception) {
287
- Log.w(NAME, "Could not unregister receiver: ${e.message}")
308
+ logW(NAME, "Could not unregister receiver: ${e.message}")
288
309
  }
289
310
  downloadReceiver = null
290
311
  isReceiverRegistered = false
@@ -319,7 +340,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
319
340
  val onProgressFuture = cachedExecutorPool.submit(onProgressCallable)
320
341
  configIdToProgressFuture[config.id] = onProgressFuture
321
342
  } catch (e: Exception) {
322
- Log.e(NAME, "resumeTasks: ${Log.getStackTraceString(e)}")
343
+ logE(NAME, "resumeTasks: ${Log.getStackTraceString(e)}")
323
344
  }
324
345
  }.start()
325
346
  }
@@ -402,7 +423,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
402
423
  // This is a redirect
403
424
  val location = connection.getHeaderField("Location")
404
425
  if (location == null) {
405
- Log.w(NAME, "Redirect response without Location header at: $currentUrl")
426
+ logW(NAME, "Redirect response without Location header at: $currentUrl")
406
427
  break
407
428
  }
408
429
 
@@ -419,7 +440,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
419
440
  else -> location
420
441
  }
421
442
 
422
- Log.d(NAME, "Redirect ${redirectCount + 1}/$maxRedirects: $currentUrl -> $location")
443
+ logD(NAME, "Redirect ${redirectCount + 1}/$maxRedirects: $currentUrl -> $location")
423
444
  redirectCount++
424
445
  } else {
425
446
  // Not a redirect, we've found the final URL
@@ -430,18 +451,18 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
430
451
  }
431
452
 
432
453
  if (redirectCount >= maxRedirects) {
433
- Log.w(
454
+ logW(
434
455
  NAME,
435
456
  "Reached maximum redirects ($maxRedirects) for URL: $originalUrl. Final URL: $currentUrl"
436
457
  )
437
458
  } else {
438
- Log.d(NAME, "Resolved URL after $redirectCount redirects: $currentUrl")
459
+ logD(NAME, "Resolved URL after $redirectCount redirects: $currentUrl")
439
460
  }
440
461
 
441
462
  return currentUrl
442
463
 
443
464
  } catch (e: Exception) {
444
- Log.e(NAME, "Failed to resolve redirects for URL: $originalUrl. Error: ${e.message}")
465
+ logE(NAME, "Failed to resolve redirects for URL: $originalUrl. Error: ${e.message}")
445
466
  // Return original URL if redirect resolution fails
446
467
  return originalUrl
447
468
  }
@@ -474,7 +495,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
474
495
  JSONObject(it.toString()).toString()
475
496
  } ?: "{}"
476
497
  } catch (e: Exception) {
477
- Log.w(NAME, "Failed to convert metadata map to string: ${e.message}")
498
+ logW(NAME, "Failed to convert metadata map to string: ${e.message}")
478
499
  "{}"
479
500
  }
480
501
  }
@@ -503,15 +524,15 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
503
524
  }
504
525
 
505
526
  if (id == null || url == null || destination == null) {
506
- Log.e(NAME, "download: id, url and destination must be set.")
527
+ logE(NAME, "download: id, url and destination must be set.")
507
528
  return
508
529
  }
509
530
 
510
531
  // Resolve redirects if maxRedirects is specified
511
532
  if (maxRedirects > 0) {
512
- Log.d(NAME, "Resolving redirects for URL: $url (maxRedirects: $maxRedirects)")
533
+ logD(NAME, "Resolving redirects for URL: $url (maxRedirects: $maxRedirects)")
513
534
  url = resolveRedirects(url, maxRedirects, headers)
514
- Log.d(NAME, "Final resolved URL: $url")
535
+ logD(NAME, "Final resolved URL: $url")
515
536
  }
516
537
 
517
538
  val request = DownloadManager.Request(Uri.parse(url))
@@ -596,18 +617,18 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
596
617
  } else {
597
618
  // External files directory path is invalid or null
598
619
  // Try setDestinationInExternalFilesDir as fallback, then ResumableDownloader if that fails
599
- Log.w(NAME, "External files directory path may be invalid for DownloadManager: ${externalFilesDir?.absolutePath}")
620
+ logW(NAME, "External files directory path may be invalid for DownloadManager: ${externalFilesDir?.absolutePath}")
600
621
 
601
622
  try {
602
623
  // Try standard setDestinationInExternalFilesDir - may work on some devices
603
624
  request.setDestinationInExternalFilesDir(reactContext, null, filename)
604
625
  startDownloadManagerDownload(downloader.download(request))
605
- Log.d(NAME, "Using setDestinationInExternalFilesDir for download: $id")
626
+ logD(NAME, "Using setDestinationInExternalFilesDir for download: $id")
606
627
  } catch (e: Exception) {
607
628
  // DownloadManager failed - fall back to ResumableDownloader
608
629
  // This handles OnePlus and other devices that return paths like /data/local/tmp/external/
609
- Log.w(NAME, "DownloadManager failed with: ${e.message}")
610
- Log.d(NAME, "Using ResumableDownloader as fallback for download: $id")
630
+ logW(NAME, "DownloadManager failed with: ${e.message}")
631
+ logD(NAME, "Using ResumableDownloader as fallback for download: $id")
611
632
  startWithResumableDownloader()
612
633
  }
613
634
  }
@@ -620,7 +641,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
620
641
  // First check if it's an active resumable download
621
642
  if (downloader.isResumableDownload(configId)) {
622
643
  downloader.pauseResumable(configId)
623
- Log.d(NAME, "Paused resumable download: $configId")
644
+ logD(NAME, "Paused resumable download: $configId")
624
645
  return
625
646
  }
626
647
 
@@ -642,11 +663,15 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
642
663
  downloadIdToConfig.remove(downloadId)
643
664
  configIdToDownloadId.remove(configId)
644
665
  saveDownloadIdToConfigMap()
645
- Log.d(NAME, "Paused DownloadManager download: $configId")
666
+ logD(NAME, "Paused DownloadManager download: $configId")
667
+ } else {
668
+ logD(NAME, "pauseTask: Download not paused (may already be paused): $configId")
646
669
  }
670
+ } else {
671
+ logW(NAME, "pauseTask: No config found for downloadId: $downloadId")
647
672
  }
648
673
  } else {
649
- Log.w(NAME, "pauseTask: No download found for configId: $configId")
674
+ logW(NAME, "pauseTask: No download found for configId: $configId")
650
675
  }
651
676
  }
652
677
  }
@@ -657,7 +682,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
657
682
  // First check if it's a paused resumable download
658
683
  if (downloader.resumableDownloader.isPaused(configId)) {
659
684
  downloader.resumeResumable(configId, resumableDownloadListener)
660
- Log.d(NAME, "Resumed paused resumable download: $configId")
685
+ logD(NAME, "Resumed paused resumable download: $configId")
661
686
  return
662
687
  }
663
688
 
@@ -665,14 +690,14 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
665
690
  if (downloader.isPaused(configId)) {
666
691
  val resumed = downloader.resume(configId, resumableDownloadListener)
667
692
  if (resumed) {
668
- Log.d(NAME, "Resumed download via ResumableDownloader: $configId")
693
+ logD(NAME, "Resumed download via ResumableDownloader: $configId")
669
694
  } else {
670
- Log.w(NAME, "Failed to resume download: $configId")
695
+ logW(NAME, "Failed to resume download: $configId")
671
696
  }
672
697
  return
673
698
  }
674
699
 
675
- Log.w(NAME, "resumeTask: No paused download found for configId: $configId")
700
+ logW(NAME, "resumeTask: No paused download found for configId: $configId")
676
701
  }
677
702
  }
678
703
 
@@ -699,11 +724,11 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
699
724
  // Firebase Performance compatibility: Add defensive programming to prevent crashes
700
725
  // when Firebase Performance SDK is installed and uses bytecode instrumentation
701
726
 
702
- Log.d(NAME, "completeHandler called with configId: $configId")
727
+ logD(NAME, "completeHandler called with configId: $configId")
703
728
 
704
729
  // Defensive programming: Validate parameters
705
730
  if (configId.isEmpty()) {
706
- Log.w(NAME, "completeHandler: Invalid configId provided")
731
+ logW(NAME, "completeHandler: Invalid configId provided")
707
732
  return
708
733
  }
709
734
 
@@ -711,12 +736,12 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
711
736
  // Currently this method doesn't have any implementation on Android
712
737
  // as completion handlers are handled differently than iOS.
713
738
  // This defensive structure ensures Firebase Performance compatibility.
714
- Log.d(NAME, "completeHandler executed successfully for configId: $configId")
739
+ logD(NAME, "completeHandler executed successfully for configId: $configId")
715
740
 
716
741
  } catch (e: Exception) {
717
742
  // Catch any potential exceptions that might be thrown due to Firebase Performance
718
743
  // bytecode instrumentation interfering with method dispatch
719
- Log.e(NAME, "completeHandler: Exception occurred: ${Log.getStackTraceString(e)}")
744
+ logE(NAME, "completeHandler: Exception occurred: ${Log.getStackTraceString(e)}")
720
745
  }
721
746
  }
722
747
 
@@ -745,7 +770,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
745
770
  val future = setFileChangesBeforeCompletion(localUri, config.destination)
746
771
  future.get()
747
772
  } catch (e: Exception) {
748
- Log.e(NAME, "Error moving completed download file: ${e.message}")
773
+ logE(NAME, "Error moving completed download file: ${e.message}")
749
774
  // Continue with normal processing even if file move fails
750
775
  }
751
776
  }
@@ -775,7 +800,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
775
800
  }
776
801
  }
777
802
  } catch (e: Exception) {
778
- Log.e(NAME, "getExistingDownloadTasks: ${Log.getStackTraceString(e)}")
803
+ logE(NAME, "getExistingDownloadTasks: ${Log.getStackTraceString(e)}")
779
804
  }
780
805
  }
781
806
 
@@ -826,7 +851,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
826
851
  }
827
852
 
828
853
  private fun onFailedDownload(config: RNBGDTaskConfig, downloadStatus: WritableMap) {
829
- Log.e(
854
+ logE(
830
855
  NAME, "onFailedDownload: " +
831
856
  "${downloadStatus.getInt("status")}:" +
832
857
  "${downloadStatus.getInt("reason")}:" +
@@ -838,7 +863,7 @@ class RNBackgroundDownloaderModuleImpl(private val reactContext: ReactApplicatio
838
863
 
839
864
  // Enhanced handling for ERROR_CANNOT_RESUME (1008)
840
865
  if (reason == DownloadManager.ERROR_CANNOT_RESUME) {
841
- Log.w(
866
+ logW(
842
867
  NAME, "ERROR_CANNOT_RESUME detected for download: ${config.id}" +
843
868
  ". This is a known Android DownloadManager issue with larger files. " +
844
869
  "Consider restarting the download or using smaller file segments."
@@ -10,7 +10,6 @@ import android.os.Binder
10
10
  import android.os.Build
11
11
  import android.os.IBinder
12
12
  import android.os.PowerManager
13
- import android.util.Log
14
13
  import androidx.core.app.NotificationCompat
15
14
  import java.util.concurrent.ConcurrentHashMap
16
15
  import java.util.concurrent.ExecutorService
@@ -92,7 +91,7 @@ class ResumableDownloadService : Service() {
92
91
  private fun logStale(event: String) {
93
92
  val currentJob = activeDownloads[id]
94
93
  val currentGeneration = downloadGeneration[id] ?: 0
95
- Log.d(TAG, "Ignoring stale $event for $id (session: my=$sessionToken vs job=${currentJob?.sessionToken}, gen: my=$generation vs current=$currentGeneration)")
94
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Ignoring stale $event for $id (session: my=$sessionToken vs job=${currentJob?.sessionToken}, gen: my=$generation vs current=$currentGeneration)")
96
95
  }
97
96
 
98
97
  override fun onBegin(id: String, expectedBytes: Long, headers: Map<String, String>) {
@@ -112,7 +111,7 @@ class ResumableDownloadService : Service() {
112
111
  val now = System.currentTimeMillis()
113
112
  val lastLogTime = lastProgressLogTime[id] ?: 0L
114
113
  if (now - lastLogTime >= DownloadConstants.PROGRESS_LOG_INTERVAL_MS) {
115
- Log.d(TAG, "onProgress: id=$id, bytes=$bytesDownloaded, myToken=$sessionToken, jobToken=${currentJob?.sessionToken}, myGen=$generation, currentGen=$currentGeneration")
114
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "onProgress: id=$id, bytes=$bytesDownloaded, myToken=$sessionToken, jobToken=${currentJob?.sessionToken}, myGen=$generation, currentGen=$currentGeneration")
116
115
  lastProgressLogTime[id] = now
117
116
  }
118
117
 
@@ -163,7 +162,7 @@ class ResumableDownloadService : Service() {
163
162
 
164
163
  override fun onCreate() {
165
164
  super.onCreate()
166
- Log.d(TAG, "Service created")
165
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Service created")
167
166
  createNotificationChannel()
168
167
  }
169
168
 
@@ -172,7 +171,7 @@ class ResumableDownloadService : Service() {
172
171
  }
173
172
 
174
173
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
175
- Log.d(TAG, "onStartCommand: action=${intent?.action}")
174
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "onStartCommand: action=${intent?.action}")
176
175
 
177
176
  when (intent?.action) {
178
177
  ACTION_START_DOWNLOAD -> {
@@ -215,7 +214,7 @@ class ResumableDownloadService : Service() {
215
214
  }
216
215
 
217
216
  override fun onDestroy() {
218
- Log.d(TAG, "Service destroyed")
217
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Service destroyed")
219
218
  releaseWakeLock()
220
219
  executorService.shutdownNow()
221
220
  super.onDestroy()
@@ -244,7 +243,7 @@ class ResumableDownloadService : Service() {
244
243
  startByte: Long,
245
244
  totalBytes: Long
246
245
  ) {
247
- Log.d(TAG, "Starting download: $id from byte $startByte")
246
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Starting download: $id from byte $startByte")
248
247
 
249
248
  // Start foreground service if not already
250
249
  startForegroundWithNotification()
@@ -254,7 +253,7 @@ class ResumableDownloadService : Service() {
254
253
  // This ensures we can detect stale events even if job was removed and re-added
255
254
  val generation = (downloadGeneration[id] ?: 0) + 1
256
255
  downloadGeneration[id] = generation
257
- Log.d(TAG, "Download $id starting with generation $generation")
256
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Download $id starting with generation $generation")
258
257
 
259
258
  val job = DownloadJob(id, url, destination, headers, startByte, totalBytes)
260
259
  activeDownloads[id] = job
@@ -275,7 +274,7 @@ class ResumableDownloadService : Service() {
275
274
  }
276
275
 
277
276
  fun pauseDownload(id: String): Boolean {
278
- Log.d(TAG, "Pausing download: $id")
277
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Pausing download: $id")
279
278
  val result = resumableDownloader.pause(id)
280
279
  if (result) {
281
280
  updateNotification()
@@ -285,13 +284,13 @@ class ResumableDownloadService : Service() {
285
284
  }
286
285
 
287
286
  fun resumeDownload(id: String): Boolean {
288
- Log.d(TAG, "Resuming download: $id")
287
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Resuming download: $id")
289
288
 
290
289
  if (this.listener == null) return false
291
290
  val currentJob = activeDownloads[id] ?: return false
292
291
  val sessionToken = currentJob.sessionToken
293
292
  val generation = downloadGeneration[id] ?: 0
294
- Log.d(TAG, "Resuming $id with session=$sessionToken, generation=$generation")
293
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Resuming $id with session=$sessionToken, generation=$generation")
295
294
 
296
295
  // Create a validating listener wrapper (don't cleanup since job already exists)
297
296
  val serviceListener = createValidatingListener(id, sessionToken, generation, cleanupOnTerminal = false)
@@ -304,7 +303,7 @@ class ResumableDownloadService : Service() {
304
303
  }
305
304
 
306
305
  fun cancelDownload(id: String): Boolean {
307
- Log.d(TAG, "Cancelling download: $id")
306
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Cancelling download: $id")
308
307
  activeDownloads.remove(id)
309
308
  val result = resumableDownloader.cancel(id)
310
309
  stopServiceIfIdle()
@@ -324,7 +323,7 @@ class ResumableDownloadService : Service() {
324
323
  startForeground(DownloadConstants.NOTIFICATION_ID, notification)
325
324
  }
326
325
  } catch (e: Exception) {
327
- Log.e(TAG, "Failed to start foreground service: ${e.message}")
326
+ RNBackgroundDownloaderModuleImpl.logE(TAG, "Failed to start foreground service: ${e.message}")
328
327
  }
329
328
  }
330
329
 
@@ -379,7 +378,7 @@ class ResumableDownloadService : Service() {
379
378
  setReferenceCounted(false)
380
379
  acquire(DownloadConstants.WAKELOCK_TIMEOUT_MS)
381
380
  }
382
- Log.d(TAG, "WakeLock acquired")
381
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "WakeLock acquired")
383
382
  }
384
383
  }
385
384
 
@@ -387,7 +386,7 @@ class ResumableDownloadService : Service() {
387
386
  wakeLock?.let {
388
387
  if (it.isHeld) {
389
388
  it.release()
390
- Log.d(TAG, "WakeLock released")
389
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "WakeLock released")
391
390
  }
392
391
  }
393
392
  wakeLock = null
@@ -399,12 +398,12 @@ class ResumableDownloadService : Service() {
399
398
  val hasPausedInResumable = activeDownloads.keys.any { resumableDownloader.getState(it) != null }
400
399
 
401
400
  if (!hasActiveDownloads && !hasPausedInResumable) {
402
- Log.d(TAG, "No active downloads, stopping service")
401
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "No active downloads, stopping service")
403
402
  releaseWakeLock()
404
403
  stopForeground(STOP_FOREGROUND_REMOVE)
405
404
  stopSelf()
406
405
  } else {
407
- Log.d(TAG, "Service has active downloads, keeping alive")
406
+ RNBackgroundDownloaderModuleImpl.logD(TAG, "Service has active downloads, keeping alive")
408
407
  updateNotification()
409
408
  }
410
409
  }