diff --git a/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicBackend.kt b/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicBackend.kt index 97c3cd3a9..c181b7c8c 100644 --- a/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicBackend.kt +++ b/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicBackend.kt @@ -146,6 +146,8 @@ object EpicBackend : StoreBackend, EpicLaunchSupport { EpicLibrary.rememberSize(app, item.id, diskBytes) } override fun onStage(stage: String, done: Long, total: Long, items: Int, itemsTotal: Int) { + // Assembly overlaps chunk fetching; only the final install event advances the UI. + if (stage == "stream") return handle.stage(when (stage) { "check" -> DownloadStage.MANIFEST; "verify" -> DownloadStage.VERIFY; else -> DownloadStage.INSTALL }) handle.stageProgress(done, total, items, itemsTotal, bytes = stage == "install") } diff --git a/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicDownloadManager.java b/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicDownloadManager.java index 21f1dc488..9c2922f64 100644 --- a/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicDownloadManager.java +++ b/app/src/main/java/com/droiddeck/launcher/stores/epic/EpicDownloadManager.java @@ -55,6 +55,7 @@ public final class EpicDownloadManager { /** * The active stage's own count: {@code stage} is "check" (the files already there, before * anything is fetched), "verify" or "install" (bytes written), {@code done} of + * "stream" reports assembly during a bounded download without completing the install stage. * {@code total} an amount (bytes, or items when there are no bytes), {@code items} of * {@code itemsTotal} the files, when it counts files. */ @@ -247,7 +248,7 @@ public final class EpicDownloadManager { if (cancel.get()) return null; String relPath = file.filename.replace("\\", "/"); File outFile = new File(installDir, relPath); - cb.onProgress("Writing: " + (relPath.contains("/") ? relPath.substring(relPath.lastIndexOf('/') + 1) : relPath), 80 + (int) (doneFiles * 20L / totalFiles)); + cb.onProgress("Downloading chunks: " + (relPath.contains("/") ? relPath.substring(relPath.lastIndexOf('/') + 1) : relPath), (int) (completedBytes * 80L / Math.max(1, fTotalBytes))); // Like the native assembler, write to the final path and remove it on failure. // A sibling ".part" can itself be a legitimate file in the manifest. File partFile = outFile; @@ -308,9 +309,10 @@ public final class EpicDownloadManager { } doneFiles++; writtenBytes += file.fileSize(); - cb.onStage("install", writtenBytes, pendingBytes, doneFiles, totalFiles); + cb.onStage("stream", writtenBytes, pendingBytes, doneFiles, totalFiles); } deleteDir(chunkCacheDir); + cb.onStage("install", writtenBytes, pendingBytes, doneFiles, totalFiles); cb.onProgress("Complete", 100); return new Result(manifest.launchExe, manifest.buildVersion, planned).withPrereq(manifest); } catch (InstallException e) { diff --git a/app/src/test/java/com/droiddeck/launcher/stores/epic/EpicBoundedInstallTest.kt b/app/src/test/java/com/droiddeck/launcher/stores/epic/EpicBoundedInstallTest.kt index 686c6324f..30204e00f 100644 --- a/app/src/test/java/com/droiddeck/launcher/stores/epic/EpicBoundedInstallTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/stores/epic/EpicBoundedInstallTest.kt @@ -106,7 +106,7 @@ class EpicBoundedInstallTest { val callback = object : EpicDownloadManager.Callback { override fun onProgress(message: String, pct: Int) {} override fun onStage(stage: String, done: Long, total: Long, items: Int, itemsTotal: Int) { - if (stage == "install" && items == 1) cancel.set(true) + if (stage == "stream" && items == 1) cancel.set(true) } } assertNull(install(cancel, callback)) @@ -121,7 +121,7 @@ class EpicBoundedInstallTest { val cancel = AtomicBoolean() val callback = object : EpicDownloadManager.Callback { override fun onProgress(message: String, pct: Int) { - if (message.startsWith("Downloading chunks")) cancel.set(true) + if (message.startsWith("Downloading chunks (")) cancel.set(true) } } assertNull(install(cancel, callback)) @@ -156,6 +156,28 @@ class EpicBoundedInstallTest { assertNotNull(install()) } + @Test fun boundedProgressNeverReentersDownloadAfterEnteringInstall() { + val events = mutableListOf() + val callback = object : EpicDownloadManager.Callback { + override fun onProgress(message: String, pct: Int) { + if (message.startsWith("Downloading chunks")) events.add("download") + if (message.startsWith("Writing")) events.add("install") + } + override fun onBytes(done: Long, total: Long, speedBps: Long) { events.add("download") } + override fun onStage(stage: String, done: Long, total: Long, items: Int, itemsTotal: Int) { + if (stage == "install") { + assertEquals(total, done) + assertEquals(itemsTotal, items) + events.add("install") + } + } + } + assertNotNull(install(callback = callback)) + val firstInstall = events.indexOf("install") + assertTrue(firstInstall > 0) + assertFalse(events.drop(firstInstall).contains("download")) + } + @Test fun hogwartsSizedSequentialPlanFitsAvailableSpaceWithoutFullChunkCache() { val gib = 1024L * 1024 * 1024 val file = EpicDownloadManager.FileInfo()