DownloadWorker: more work
This commit is contained in:
@@ -14,8 +14,7 @@ fun InputStream.copyTo(out: OutputStream, streamSize: Long): Flow<DownloadInfo>
|
|||||||
var bytesCopied: Long = 0
|
var bytesCopied: Long = 0
|
||||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||||
var bytes = read(buffer)
|
var bytes = read(buffer)
|
||||||
|
var lastTotalBytesRead: Long = 0
|
||||||
var lastTotalBytesRead = 0L
|
|
||||||
var speed: Long = 0
|
var speed: Long = 0
|
||||||
@Suppress("KotlinConstantConditions") // False-positive for bytesCopied always being zero
|
@Suppress("KotlinConstantConditions") // False-positive for bytesCopied always being zero
|
||||||
val timer = fixedRateTimer("timer", true, 0L, 1000) {
|
val timer = fixedRateTimer("timer", true, 0L, 1000) {
|
||||||
@@ -26,6 +25,8 @@ fun InputStream.copyTo(out: OutputStream, streamSize: Long): Flow<DownloadInfo>
|
|||||||
|
|
||||||
while (bytes >= 0) {
|
while (bytes >= 0) {
|
||||||
out.write(buffer, 0, bytes)
|
out.write(buffer, 0, bytes)
|
||||||
|
out.flush()
|
||||||
|
|
||||||
bytesCopied += bytes
|
bytesCopied += bytes
|
||||||
// Emit stream progress in percentage
|
// Emit stream progress in percentage
|
||||||
emit(DownloadInfo((bytesCopied * 100 / streamSize).toInt(), bytes.toLong(), speed))
|
emit(DownloadInfo((bytesCopied * 100 / streamSize).toInt(), bytes.toLong(), speed))
|
||||||
|
|||||||
@@ -188,7 +188,12 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
): Boolean = withContext(Dispatchers.IO) {
|
): Boolean = withContext(Dispatchers.IO) {
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
try {
|
try {
|
||||||
downloadFile(file)
|
val success = downloadFile(file)
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
Log.w(TAG, "Download failed for $packageName")
|
||||||
|
return@withContext false
|
||||||
|
}
|
||||||
|
|
||||||
if (isStopped) {
|
if (isStopped) {
|
||||||
Log.w(TAG, "Download cancelled for $packageName")
|
Log.w(TAG, "Download cancelled for $packageName")
|
||||||
@@ -288,51 +293,51 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
* Downloads the file from the given request.
|
* Downloads the file from the given request.
|
||||||
* Failed downloads aren't removed and persists as long as [CacheWorker] doesn't cleans them.
|
* Failed downloads aren't removed and persists as long as [CacheWorker] doesn't cleans them.
|
||||||
* @param gFile A [GPlayFile] to download
|
* @param gFile A [GPlayFile] to download
|
||||||
* @return A [Result] indicating whether the file was downloaded or not.
|
* @return A [Boolean] indicating whether the file was downloaded or not.
|
||||||
*/
|
*/
|
||||||
private suspend fun downloadFile(gFile: GPlayFile): Result {
|
private suspend fun downloadFile(gFile: GPlayFile): Boolean = withContext(Dispatchers.IO) {
|
||||||
Log.i(TAG, "Downloading ${gFile.name}")
|
Log.i(TAG, "Downloading ${gFile.name}")
|
||||||
return withContext(Dispatchers.IO) {
|
val file = PathUtil.getLocalFile(appContext, gFile, download)
|
||||||
val file = PathUtil.getLocalFile(appContext, gFile, download)
|
|
||||||
|
|
||||||
// If file exists and has integrity intact, no need to download again
|
// If file exists and has integrity intact, no need to download again
|
||||||
if (file.exists() && verifyFile(gFile)) {
|
if (file.exists() && verifyFile(gFile)) {
|
||||||
Log.i(TAG, "$file is already downloaded!")
|
Log.i(TAG, "$file is already downloaded!")
|
||||||
downloadedBytes += file.length()
|
downloadedBytes += file.length()
|
||||||
return@withContext Result.success()
|
return@withContext true
|
||||||
|
}
|
||||||
|
|
||||||
|
val tmpFileSuffix = ".tmp"
|
||||||
|
val tmpFile = File(file.absolutePath + tmpFileSuffix)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Download as a temporary file to avoid installing corrupted files
|
||||||
|
val isNewFile = tmpFile.createNewFile()
|
||||||
|
|
||||||
|
val okHttpClient = httpClient as HttpClient
|
||||||
|
val headers = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
if (!isNewFile) {
|
||||||
|
Log.i(TAG, "$tmpFile has an unfinished download, resuming!")
|
||||||
|
downloadedBytes += tmpFile.length()
|
||||||
|
headers["Range"] = "bytes=${tmpFile.length()}-"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
okHttpClient.call(gFile.url, headers).body?.byteStream()?.use { input ->
|
||||||
// Download as a temporary file to avoid installing corrupted files
|
FileOutputStream(tmpFile, !isNewFile).use {
|
||||||
val tmpFileSuffix = ".tmp"
|
input.copyTo(it, gFile.size).collect { info -> onProgress(info) }
|
||||||
val tmpFile = File(file.absolutePath + tmpFileSuffix)
|
|
||||||
val isNewFile = tmpFile.createNewFile()
|
|
||||||
|
|
||||||
val okHttpClient = httpClient as HttpClient
|
|
||||||
val headers = mutableMapOf<String, String>()
|
|
||||||
|
|
||||||
if (!isNewFile) {
|
|
||||||
Log.i(TAG, "$tmpFile has an unfinished download, resuming!")
|
|
||||||
downloadedBytes += tmpFile.length()
|
|
||||||
headers["Range"] = "bytes=${tmpFile.length()}-"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
okHttpClient.call(gFile.url, headers).body?.byteStream()?.use { input ->
|
|
||||||
FileOutputStream(tmpFile, !isNewFile).use {
|
|
||||||
input.copyTo(it, gFile.size).collect { p -> onProgress(p) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tmpFile.renameTo(file)) {
|
|
||||||
throw Exception("Failed to remove .tmp extension from $tmpFile")
|
|
||||||
}
|
|
||||||
|
|
||||||
return@withContext Result.success()
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Log.e(TAG, "Failed to download $file!", exception)
|
|
||||||
notifyStatus(DownloadStatus.FAILED)
|
|
||||||
return@withContext Result.failure()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tmpFile.renameTo(file)) {
|
||||||
|
throw Exception("Failed to remove .tmp extension from $tmpFile")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withContext true
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Failed to download $file!", exception)
|
||||||
|
tmpFile.delete()
|
||||||
|
notifyStatus(DownloadStatus.FAILED)
|
||||||
|
return@withContext true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user