Skip to content

Commit 03d7145

Browse files
authored
Merge pull request #1291 from joshuatam/fix/gog-download-pause-resume
fix: gog download pause / resume handling logic
2 parents cbea7f7 + 88c1ef4 commit 03d7145

2 files changed

Lines changed: 38 additions & 6 deletions

File tree

app/src/main/java/app/gamenative/service/gog/GOGDownloadManager.kt

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,10 @@ class GOGDownloadManager @Inject constructor(
852852
networkChunkFlow
853853
.flatMapMerge<String, Unit>(concurrency = parallelDownloads) { chunkMd5 ->
854854
flow<Unit> {
855+
if (!downloadInfo.isActive()) {
856+
return@flow
857+
}
858+
855859
val result = run {
856860
val urls = currentChunkUrlCandidates[chunkMd5] ?: return@run Result.failure<File>(
857861
Exception("No URL candidates found for chunk $chunkMd5"),
@@ -872,6 +876,10 @@ class GOGDownloadManager @Inject constructor(
872876
assembleFlow
873877
.flatMapMerge<Pair<String, Result<File>>, Unit>(concurrency = parallelAssemble) { (chunkMd5, result) ->
874878
flow<Unit> {
879+
if (!downloadInfo.isActive()) {
880+
return@flow
881+
}
882+
875883
if (result.isSuccess && assemblyFailure == null) {
876884
// Successful download - add to completed set and try assembly
877885
downloadedChunkIds.add(chunkMd5)
@@ -943,6 +951,10 @@ class GOGDownloadManager @Inject constructor(
943951
val chunksAdded = mutableListOf<String>()
944952

945953
files.forEach { file ->
954+
if (!downloadInfo.isActive()) {
955+
Timber.tag("GOG").w("Download cancelled during file iteration")
956+
return@launch
957+
}
946958
Timber.tag("GOG").v("Pre-allocating ${file.path}")
947959

948960
// Allocating file before download
@@ -975,6 +987,12 @@ class GOGDownloadManager @Inject constructor(
975987
var currentPendingChunks = lastPendingChunks
976988
var samePendingChunksAttempts = 0
977989
while (currentPendingChunks > 0) {
990+
if (!downloadInfo.isActive()) {
991+
networkChunkJob.cancel()
992+
assembleJob.cancel()
993+
return@withContext Result.failure(Exception("Download cancelled"))
994+
}
995+
978996
Timber.tag("GOG").d("Waiting for $currentPendingChunks pending chunks to complete")
979997

980998
if (currentPendingChunks == lastPendingChunks) {
@@ -1009,9 +1027,6 @@ class GOGDownloadManager @Inject constructor(
10091027
// Cancel the assemble flow jobs since no more files will be added
10101028
assembleJob.cancel()
10111029

1012-
// Remove the cache dir
1013-
chunkCacheDir.deleteRecursively()
1014-
10151030
if (assemblyFailure != null) {
10161031
return@withContext Result.failure(assemblyFailure!!)
10171032
}
@@ -1289,6 +1304,10 @@ class GOGDownloadManager @Inject constructor(
12891304
var lastException: Exception? = null
12901305

12911306
repeat(MAX_CHUNK_RETRIES) { attempt ->
1307+
if (!downloadInfo.isActive()) {
1308+
return@withContext Result.failure(Exception("Download cancelled"))
1309+
}
1310+
12921311
val url = urlCandidates[attempt % urlCandidates.size]
12931312
val result = downloadChunk(chunkMd5, url, chunkCacheDir, downloadInfo, httpClient)
12941313

@@ -1303,8 +1322,11 @@ class GOGDownloadManager @Inject constructor(
13031322

13041323
if (attempt < MAX_CHUNK_RETRIES - 1) {
13051324
val delay = RETRY_DELAY_MS * (1 shl attempt) // Exponential backoff: 1s, 2s, 4s
1306-
Timber.tag("GOG").w("Chunk $chunkMd5 download failed (attempt ${attempt + 1}/$MAX_CHUNK_RETRIES): ${lastException?.message}. Retrying in ${delay}ms...")
1307-
kotlinx.coroutines.delay(delay)
1325+
if (downloadInfo.isActive()) {
1326+
Timber.tag("GOG")
1327+
.w("Chunk $chunkMd5 download failed (attempt ${attempt + 1}/$MAX_CHUNK_RETRIES): ${lastException?.message}. Retrying in ${delay}ms...")
1328+
delay(delay)
1329+
}
13081330
}
13091331
}
13101332

@@ -1328,6 +1350,10 @@ class GOGDownloadManager @Inject constructor(
13281350
httpClient: OkHttpClient,
13291351
): Result<File> = withContext(Dispatchers.IO) {
13301352
try {
1353+
if (!downloadInfo.isActive()) {
1354+
return@withContext Result.failure(Exception("Download cancelled"))
1355+
}
1356+
13311357
val chunkFile = File(chunkCacheDir, "$chunkMd5.chunk")
13321358
val tempChunkFile = File(chunkCacheDir, "$chunkMd5.chunk.part")
13331359

@@ -1580,6 +1606,7 @@ class GOGDownloadManager @Inject constructor(
15801606
/**
15811607
* Check if file exists and has the expected size. When [expectedMd5] is non-null/non-blank,
15821608
* also verifies content MD5 to reject corrupted files; short-circuits on size mismatch before hashing.
1609+
* When [expectedMd5] is null/blank, returns false to avoid treating pre-allocated files as complete.
15831610
*/
15841611
private fun fileExistsWithCorrectSize(
15851612
outputFile: File,
@@ -1588,7 +1615,8 @@ class GOGDownloadManager @Inject constructor(
15881615
): Boolean {
15891616
if (!outputFile.exists()) return false
15901617
if (outputFile.length() != expectedSize) return false
1591-
return expectedMd5.isNullOrBlank() || calculateMd5File(outputFile).equals(expectedMd5, ignoreCase = true)
1618+
if (expectedMd5.isNullOrBlank()) return false
1619+
return calculateMd5File(outputFile).equals(expectedMd5, ignoreCase = true)
15921620
}
15931621
/**
15941622
* Calculate MD5 hash of file

app/src/test/java/app/gamenative/service/gog/GOGDownloadManagerTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app.gamenative.service.gog
22

33
import android.content.Context
4+
import app.gamenative.PrefManager
45
import app.gamenative.data.DownloadInfo
56
import app.gamenative.data.GOGGame
67
import app.gamenative.service.gog.api.BuildsResponse
@@ -33,6 +34,7 @@ import org.mockito.kotlin.mock
3334
import org.mockito.kotlin.verify
3435
import org.mockito.kotlin.whenever
3536
import org.robolectric.RobolectricTestRunner
37+
import org.robolectric.RuntimeEnvironment
3638
import org.robolectric.annotation.Config
3739

3840
@OptIn(ExperimentalCoroutinesApi::class)
@@ -52,6 +54,8 @@ class GOGDownloadManagerTest {
5254
gogManager = mock()
5355
context = mock()
5456
manager = GOGDownloadManager(apiClient, parser, gogManager, context)
57+
PrefManager.init(RuntimeEnvironment.getApplication())
58+
PrefManager.downloadSpeed = 32
5559
}
5660

5761
// ===== Gen 2 =====

0 commit comments

Comments
 (0)