Default fetch config.
Test url: https://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=730602
(Click download after load the page)
Fetch 3.1.3 => download.getTotal() retruns -1
Fetch 3.1.2 => download.getTotal() retruns 50.9MB which is correct

Working perfectly
Please share some code to better understand
implementation "androidx.tonyodev.fetch2:xfetch2:3.1.3"
implementation "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:3.1.3"
implementation 'com.squareup.okhttp3:okhttp:4.0.0'
final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
.setDownloadConcurrentLimit(3)
.enableRetryOnNetworkGain(true)
.enableAutoStart(false)
.setAutoRetryMaxAttempts(10)
.setProgressReportingInterval(1100)
.setHttpDownloader(new OkHttpDownloader(okHttpClient, ownloader.FileDownloaderType.PARALLEL))
.build();
I fetch the file size (content-length) manually using okhttp (not using fetch.getcContentLength()) before start download. (For show to the user). Then start the download. I update the progress in recyclerview adapter (download.getTotal()).
You have to create Custom downloader class. Like this one
class CustomDownloader(okHttpClient: OkHttpClient) : OkHttpDownloader(okHttpClient) {
private fun getResponseHeaders(okResponseHeaders: Headers): MutableMap<String, List<String>> {
val headers = mutableMapOf<String, List<String>>()
for (i in 0 until okResponseHeaders.size()) {
val key = okResponseHeaders.name(i)
val values = okResponseHeaders.values(key)
headers[key] = values
}
return headers
}
private fun getRedirectedServerRequest(oldRequest: Downloader.ServerRequest, redirectUrl: String): Downloader.ServerRequest {
return Downloader.ServerRequest(
id = oldRequest.id,
url = oldRequest.url,
headers = oldRequest.headers,
file = oldRequest.file,
fileUri = oldRequest.fileUri,
tag = oldRequest.tag,
identifier = oldRequest.identifier,
requestMethod = oldRequest.requestMethod,
extras = oldRequest.extras,
redirected = true,
redirectUrl = redirectUrl,
segment = oldRequest.segment)
}
fun getContentLengthFromHeader(headers: Map<String, List<String>>, defaultValue: Long): Long {
var contentLength = defaultValue
if (headers.containsKey("Content-Length")) {
val size = headers["Content-Length"]?.firstOrNull()?.toLongOrNull()
if (size != null && size > 0) {
contentLength = size
return contentLength
}
}
if (headers.containsKey("content-length")) {
val size = headers["content-length"]?.firstOrNull()?.toLongOrNull()
if (size != null && size > 0) {
contentLength = size
return contentLength
}
}
if (headers.containsKey("Content-Range")) {
val value = headers["Content-Range"]?.firstOrNull()
if (value != null) {
val index = value.lastIndexOf("/")
if (index != -1 && ((index + 1) < value.length)) {
val sizeText = value.substring(index + 1)
val size = sizeText.toLongOrNull()
if (size != null && size > 0) {
contentLength = size
return contentLength
}
}
}
}
if (headers.containsKey("content-range")) {
val value = headers["content-range"]?.firstOrNull()
if (value != null) {
val index = value.lastIndexOf("/")
if (index != -1 && ((index + 1) < value.length)) {
val sizeText = value.substring(index + 1)
val size = sizeText.toLongOrNull()
if (size != null && size > 0) {
contentLength = size
return contentLength
}
}
}
}
return contentLength
}
override fun execute(request: Downloader.ServerRequest, interruptMonitor: InterruptMonitor): Downloader.Response? {
var okHttpRequest = onPreClientExecute(client, request)
if (okHttpRequest.header("Referer") == null) {
val referer = getRefererFromUrl(request.url)
okHttpRequest = okHttpRequest.newBuilder()
.addHeader("Referer", referer)
.build()
}
var okHttpResponse = client.newCall(okHttpRequest).execute()
var responseHeaders = getResponseHeaders(okHttpResponse.headers())
var code = okHttpResponse.code()
if ((code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER) && responseHeaders.containsKey("Location")) {
okHttpRequest = onPreClientExecute(client, getRedirectedServerRequest(request,
responseHeaders["Location"]?.firstOrNull() ?: ""))
if (okHttpRequest.header("Referer") == null) {
val referer = getRefererFromUrl(request.url)
okHttpRequest = okHttpRequest.newBuilder()
.addHeader("Referer", referer)
.build()
}
okHttpResponse = client.newCall(okHttpRequest).execute()
responseHeaders = getResponseHeaders(okHttpResponse.headers())
code = okHttpResponse.code()
}
val success = okHttpResponse.isSuccessful
var contentLength = getContentLengthFromHeader(responseHeaders, -1)
val byteStream: InputStream? = okHttpResponse.body()?.byteStream()
val errorResponseString: String? = if (!success) {
copyStreamToString(byteStream, false)
} else {
null
}
val hash = getContentHash(responseHeaders)
if (contentLength < 1) {
contentLength = responseHeaders["Content-Length"]?.firstOrNull()?.toLong() ?: -1L
}
val acceptsRanges = code == HttpURLConnection.HTTP_PARTIAL ||
responseHeaders["Accept-Ranges"]?.firstOrNull() == "bytes"
onServerResponse(request, Downloader.Response(
code = code,
isSuccessful = success,
contentLength = contentLength,
byteStream = null,
request = request,
hash = hash,
responseHeaders = responseHeaders,
acceptsRanges = acceptsRanges,
errorResponse = errorResponseString))
val response = Downloader.Response(
code = code,
isSuccessful = success,
contentLength = contentLength,
byteStream = byteStream,
request = request,
hash = hash,
responseHeaders = responseHeaders,
acceptsRanges = acceptsRanges,
errorResponse = errorResponseString)
connections[response] = okHttpResponse
return response
}
override fun getRequestFileDownloaderType(request: Downloader.ServerRequest, supportedFileDownloaderTypes: Set<Downloader.FileDownloaderType>): Downloader.FileDownloaderType {
return Downloader.FileDownloaderType.PARALLEL
}
}
and then use it in fetch configuration
final OkHttpClient client = new OkHttpClient.Builder().build();
final Downloader customDownloader = new CustomDownloader(client);
final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
.enableRetryOnNetworkGain(true)
.setDownloadConcurrentLimit(Integer.parseInt(sharedPreferences.getString("simultaneous_download","9")))
.createDownloadFileOnEnqueue(true)
.preAllocateFileOnCreation(true)
.enableLogging(true)
.setLogger(new FetchLogger())
.setProgressReportingInterval(1000)
.setPrioritySort(PrioritySort.ASC)
.setHttpDownloader(customDownloader)
.build();
fetch = Fetch.Impl.getInstance(fetchConfiguration);
The issue you are facing will be fixed in next release as @tonyofrancis already push commit https://github.com/tonyofrancis/Fetch/commit/136bf515548e34557d9389c8e285cb70f1a89773
related to this issue.
Follow this issue for more info https://github.com/tonyofrancis/Fetch/issues/396
I use 3.1.2 for now, because custom download manager is not needed for my purpose. @maulik9898 Thank you very much for your timely clarification.
@jpvs0101 fixed in next release
@jpvs0101 This issue is now fixed in new Fetch version 3.0.10/Androidx 3.1.4.