Skip to content

Commit 413ab53

Browse files
committed
update more dependencies
1 parent 1e77adc commit 413ab53

4 files changed

Lines changed: 159 additions & 83 deletions

File tree

src/desktopMain/kotlin/nestdrop/NestdropControl.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,8 @@ sealed interface NestdropControl {
227227
}
228228

229229
override suspend fun startFlows() {
230-
combine(minState, maxState) { min, max ->
231-
val min = min(min, max)
232-
val max = max(min, max)
233-
min.coerceIn(range.start, range.endInclusive) to max.coerceIn(range.start, range.endInclusive)
230+
combine(minState, maxState) { a, b ->
231+
min(a, b).coerceIn(range) to max(a, b).coerceIn(range)
234232
}
235233
.onEach { (minValue, maxValue) ->
236234
valueLabel.value = "${round(minValue)} - ${round(maxValue)}"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package ui
2+
3+
import io.github.kdroidfilter.platformtools.getCacheDir
4+
import io.github.kdroidfilter.platformtools.releasefetcher.downloader.ReleaseFetcherConfig
5+
import io.github.oshai.kotlinlogging.KotlinLogging
6+
import io.ktor.client.HttpClient
7+
import io.ktor.client.call.*
8+
import io.ktor.client.engine.cio.CIO
9+
import io.ktor.client.plugins.*
10+
import io.ktor.client.plugins.HttpTimeoutConfig
11+
import io.ktor.client.request.*
12+
import io.ktor.http.*
13+
import io.ktor.utils.io.*
14+
import kotlinx.coroutines.Dispatchers
15+
import kotlinx.coroutines.withContext
16+
import java.io.File
17+
18+
private val logger = KotlinLogging.logger {}
19+
20+
21+
/**
22+
* Downloader is responsible for handling file downloads from a given URL.
23+
* This class provides functionality to asynchronously download files, report
24+
* download progress, and handle errors that may occur during the download process.
25+
*/
26+
class Downloader {
27+
val client = HttpClient(CIO) {
28+
followRedirects = true
29+
30+
install(HttpTimeout) {
31+
requestTimeoutMillis = 30_000
32+
// requestTimeoutMillis = HttpTimeoutConfig.INFINITE_TIMEOUT_MS
33+
}
34+
}
35+
36+
private val downloaderBufferSize = 2 * 1024 * 1024
37+
38+
/**
39+
* Downloads an application file from the provided URL and tracks the download progress.
40+
*
41+
* @param downloadUrl The URL from which the application will be downloaded.
42+
* @param onProgress A callback function reporting download progress percentage and the downloaded file (if available).
43+
* The percentage is a `Double` ranging from 0.0 to 100.0, or -1.0 in case of errors.
44+
* The `file` parameter is the local file being downloaded, or `null` during download progress updates.
45+
* @return A `Boolean` indicating whether the download was successful.
46+
* Returns `true` if the download completes successfully, otherwise `false`.
47+
*/
48+
suspend fun downloadApp(
49+
downloadUrl: String,
50+
onProgress: (percentage: Double, file: File?) -> Unit
51+
): Boolean {
52+
val bufferSize = downloaderBufferSize
53+
logger.debug { "Starting download from URL: $downloadUrl" }
54+
55+
val fileName = downloadUrl.substringAfterLast('/').substringBefore('?')
56+
val cacheDir = getCacheDir()
57+
val destinationFile = File(cacheDir, fileName)
58+
59+
onProgress(0.0, null)
60+
logger.debug { "Download initialized: 0%" }
61+
62+
return try {
63+
val response = client.get(downloadUrl) {
64+
onDownload { bytesSentTotal, contentLength ->
65+
val progress = if (contentLength != null && contentLength > 0) {
66+
(bytesSentTotal * 100.0 / contentLength)
67+
} else 0.0
68+
logger.debug { "Progress: $bytesSentTotal / $contentLength bytes" }
69+
onProgress(progress, null)
70+
}
71+
}
72+
73+
if (response.status.isSuccess()) {
74+
val channel: ByteReadChannel = response.body()
75+
val contentLength = response.contentLength() ?: -1L
76+
logger.debug { "Content length: $contentLength bytes" }
77+
78+
withContext(Dispatchers.IO) {
79+
destinationFile.outputStream().buffered(bufferSize).use { output ->
80+
val buffer = ByteArray(bufferSize)
81+
while (!channel.isClosedForRead) {
82+
val bytesRead = channel.readAvailable(buffer)
83+
if (bytesRead == -1) break
84+
output.write(buffer, 0, bytesRead)
85+
}
86+
}
87+
}
88+
89+
if (destinationFile.exists()) {
90+
logger.debug { "Download completed. Size: ${destinationFile.length()} bytes" }
91+
onProgress(100.0, destinationFile)
92+
true
93+
} else {
94+
logger.error { "Error: File not created" }
95+
onProgress(-1.0, null) // Keep -1.0 for errors
96+
false
97+
}
98+
} else {
99+
logger.error { "Download failed: ${response.status}" }
100+
onProgress(-1.0, null) // Keep -1.0 for errors
101+
false
102+
}
103+
} catch (e: Exception) {
104+
logger.error(e) { "Download error: ${e.message}" }
105+
onProgress(-1.0, null) // Keep -1.0 for errors
106+
false
107+
}
108+
}
109+
}

src/desktopMain/kotlin/ui/UpdateCheckerUI.kt

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ import androidx.compose.material.MaterialTheme
1010
import androidx.compose.material.Text
1111
import androidx.compose.material.icons.Icons
1212
import androidx.compose.material.icons.outlined.BrowserUpdated
13-
import androidx.compose.material.icons.outlined.SystemUpdate
1413
import androidx.compose.material3.LinearProgressIndicator
1514
import androidx.compose.runtime.*
16-
import androidx.compose.ui.Alignment
1715
import androidx.compose.ui.Modifier
18-
import androidx.compose.ui.text.style.TextAlign
1916
import androidx.compose.ui.unit.dp
17+
import io.github.kdroidfilter.platformtools.OperatingSystem
2018
import io.github.kdroidfilter.platformtools.appmanager.getAppInstaller
21-
import io.github.kdroidfilter.platformtools.releasefetcher.downloader.Downloader
19+
import io.github.kdroidfilter.platformtools.appmanager.restartApplication
20+
import io.github.kdroidfilter.platformtools.getAppVersion
21+
import io.github.kdroidfilter.platformtools.getOperatingSystem
2222
import io.github.kdroidfilter.platformtools.releasefetcher.github.GitHubReleaseFetcher
23+
import io.github.kdroidfilter.platformtools.releasefetcher.github.model.Release
2324
import io.github.oshai.kotlinlogging.KotlinLogging
25+
import io.github.z4kn4fein.semver.toVersion
2426
import kotlinx.coroutines.CoroutineScope
2527
import kotlinx.coroutines.Dispatchers
2628
import kotlinx.coroutines.launch
@@ -183,7 +185,7 @@ fun UpdateCheckerUI(fetcher: GitHubReleaseFetcher) {
183185
CoroutineScope(Dispatchers.IO).launch {
184186
isDownloading = true
185187
val release = fetcher.getLatestRelease()
186-
val downloadLink = release?.let { fetcher.getDownloadLinkForPlatform(it) }
188+
val downloadLink = release?.getDownloadLinkForPlatform()
187189
if (downloadLink != null) {
188190
val downloader = Downloader()
189191
downloader.downloadApp(downloadLink) { progress, file ->
@@ -305,7 +307,8 @@ fun UpdateCheckerUI(fetcher: GitHubReleaseFetcher) {
305307
},
306308
confirmButton = {
307309
Button(onClick = {
308-
exitProcess(0)
310+
restartApplication()
311+
// exitProcess(0)
309312
//TODO The restartApplication() function not work after an update
310313
}) {
311314
Text("Quit Now")
@@ -334,4 +337,33 @@ fun UpdateCheckerUI(fetcher: GitHubReleaseFetcher) {
334337
fun hasInstallPermission(): Boolean = true
335338
fun requestInstallPermission(onGranted: () -> Unit, onDenied: () -> Unit) {
336339
onGranted()
340+
}
341+
342+
fun Release.getDownloadLinkForPlatform(): String? {
343+
val operatingSystemFileTypes = mapOf(
344+
OperatingSystem.ANDROID to ".apk",
345+
OperatingSystem.WINDOWS to ".msi",
346+
OperatingSystem.LINUX to ".deb",
347+
OperatingSystem.MACOS to ".dmg"
348+
)
349+
350+
val fileType = operatingSystemFileTypes[getOperatingSystem()] ?: return null
351+
352+
// Find the corresponding asset
353+
val asset = assets.firstOrNull { it.name.endsWith(fileType, ignoreCase = true) }
354+
return asset?.browser_download_url
355+
}
356+
357+
suspend fun GitHubReleaseFetcher.checkForUpdate(
358+
onUpdateNeeded: (latestVersion: String, changelog: String) -> Unit,
359+
) {
360+
val latestRelease = getLatestRelease()
361+
if (latestRelease != null) {
362+
val currentVersion = getAppVersion().toVersion(strict = false)
363+
val latestVersion = latestRelease.tag_name.toVersion(strict = false)
364+
365+
if (latestVersion > currentVersion) {
366+
onUpdateNeeded(latestVersion.toString(), latestRelease.body.orEmpty())
367+
}
368+
}
337369
}

versions.properties

Lines changed: 10 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,86 +17,23 @@ plugin.org.jetbrains.compose=1.9.3
1717
## # available=1.10.0-rc02
1818
## # available=1.11.0-alpha01
1919

20-
plugin.dev.reformator.stacktracedecoroutinator=2.5.7
21-
## # available=2.5.8
22-
## # available=2.5.9
23-
## # available=2.6.0
24-
## # available=2.6.1
25-
26-
version.ch.qos.logback..logback-classic=1.5.18
27-
## # available=1.5.19
28-
## # available=1.5.20
29-
## # available=1.5.21
30-
## # available=1.5.22
31-
## # available=1.5.23
32-
## # available=1.5.24
20+
plugin.dev.reformator.stacktracedecoroutinator=2.6.1
21+
22+
version.ch.qos.logback..logback-classic=1.5.24
3323

3424
version.com.akuleshov7..ktoml-core=0.7.1
3525

3626
version.com.github.doyaaaaaken..kotlin-csv-jvm=1.10.0
3727

3828
version.io.github.cdimascio..dotenv-kotlin=6.5.1
3929

40-
version.io.github.kdroidfilter..platformtools.appmanager=0.3.3
41-
## # available=0.3.4
42-
## # available=0.3.5
43-
## # available=0.4.0
44-
## # available=0.5.0
45-
## # available=0.6.0
46-
## # available=0.6.1
47-
## # available=0.6.2
48-
## # available=0.7.0
49-
## # available=0.7.1
50-
## # available=0.7.2
51-
## # available=0.7.3
52-
## # available=0.7.4
53-
54-
version.io.github.kdroidfilter..platformtools.core=0.3.3
55-
## # available=0.3.4
56-
## # available=0.3.5
57-
## # available=0.4.0
58-
## # available=0.5.0
59-
## # available=0.6.0
60-
## # available=0.6.1
61-
## # available=0.6.2
62-
## # available=0.7.0
63-
## # available=0.7.1
64-
## # available=0.7.2
65-
## # available=0.7.3
66-
## # available=0.7.4
67-
68-
version.io.github.kdroidfilter..platformtools.darkmodedetector=0.3.3
69-
## # available=0.3.4
70-
## # available=0.3.5
71-
## # available=0.4.0
72-
## # available=0.5.0
73-
## # available=0.6.0
74-
## # available=0.6.1
75-
## # available=0.6.2
76-
## # available=0.7.0
77-
## # available=0.7.1
78-
## # available=0.7.2
79-
## # available=0.7.3
80-
## # available=0.7.4
81-
82-
## unused
83-
version.io.github.kdroidfilter..platformtools.permissionhandler=0.3.3
84-
## # available=0.3.4
85-
## # available=0.3.5
86-
87-
version.io.github.kdroidfilter..platformtools.releasefetcher=0.3.3
88-
## # available=0.3.4
89-
## # available=0.3.5
90-
## # available=0.4.0
91-
## # available=0.5.0
92-
## # available=0.6.0
93-
## # available=0.6.1
94-
## # available=0.6.2
95-
## # available=0.7.0
96-
## # available=0.7.1
97-
## # available=0.7.2
98-
## # available=0.7.3
99-
## # available=0.7.4
30+
version.io.github.kdroidfilter..platformtools.appmanager=0.7.4
31+
32+
version.io.github.kdroidfilter..platformtools.core=0.7.4
33+
34+
version.io.github.kdroidfilter..platformtools.darkmodedetector=0.7.4
35+
36+
version.io.github.kdroidfilter..platformtools.releasefetcher=0.7.4
10037

10138
version.io.projectreactor.tools..blockhound=1.0.15.RELEASE
10239

0 commit comments

Comments
 (0)