diff --git a/app/src/github/java/io/nekohasekai/sfa/vendor/ApkDownloader.kt b/app/src/github/java/io/nekohasekai/sfa/vendor/ApkDownloader.kt index 98ce882..d90e2c7 100644 --- a/app/src/github/java/io/nekohasekai/sfa/vendor/ApkDownloader.kt +++ b/app/src/github/java/io/nekohasekai/sfa/vendor/ApkDownloader.kt @@ -1,5 +1,6 @@ package io.nekohasekai.sfa.vendor +import io.nekohasekai.libbox.HTTPResponseWriteToProgressHandler import io.nekohasekai.libbox.Libbox import io.nekohasekai.sfa.Application import io.nekohasekai.sfa.update.UpdateState @@ -27,7 +28,15 @@ class ApkDownloader : Closeable { request.setURL(url) val response = request.execute() - response.writeTo(apkFile.absolutePath) + response.writeToWithProgress( + apkFile.absolutePath, + object : HTTPResponseWriteToProgressHandler { + override fun update(progress: Long, total: Long) { + UpdateState.downloadProgress.value = + if (total > 0) progress.toFloat() / total.toFloat() else null + } + }, + ) if (!apkFile.exists() || apkFile.length() == 0L) { throw Exception("Download failed: empty file") diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt index 574422b..249d12c 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt @@ -42,6 +42,7 @@ import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.NavigationBar @@ -565,10 +566,22 @@ class MainActivity : color = MaterialTheme.colorScheme.error, ) } else { - Row(verticalAlignment = Alignment.CenterVertically) { - CircularProgressIndicator(modifier = Modifier.size(24.dp)) - Spacer(modifier = Modifier.width(12.dp)) - Text(stringResource(R.string.downloading)) + val progress by UpdateState.downloadProgress + Column { + if (progress != null) { + Text("${stringResource(R.string.downloading)} ${(progress!! * 100).toInt()}%") + } else { + Text(stringResource(R.string.downloading)) + } + Spacer(modifier = Modifier.height(8.dp)) + if (progress != null) { + LinearProgressIndicator( + progress = { progress!! }, + modifier = Modifier.fillMaxWidth(), + ) + } else { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } } } } @@ -580,6 +593,7 @@ class MainActivity : downloadJob = null showDownloadDialog = false downloadError = null + UpdateState.downloadProgress.value = null }, ) { Text(stringResource(if (downloadError != null) R.string.ok else android.R.string.cancel)) diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt index 99c82d2..3a71f46 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme @@ -261,10 +262,22 @@ fun AppSettingsScreen(navController: NavController) { color = MaterialTheme.colorScheme.error, ) } else { - Row(verticalAlignment = Alignment.CenterVertically) { - CircularProgressIndicator(modifier = Modifier.size(24.dp)) - Spacer(modifier = Modifier.width(12.dp)) - Text(stringResource(R.string.downloading)) + val progress by UpdateState.downloadProgress + Column { + if (progress != null) { + Text("${stringResource(R.string.downloading)} ${(progress!! * 100).toInt()}%") + } else { + Text(stringResource(R.string.downloading)) + } + Spacer(modifier = Modifier.height(8.dp)) + if (progress != null) { + LinearProgressIndicator( + progress = { progress!! }, + modifier = Modifier.fillMaxWidth(), + ) + } else { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } } } } @@ -276,6 +289,7 @@ fun AppSettingsScreen(navController: NavController) { downloadJob = null showDownloadDialog = false downloadError = null + UpdateState.downloadProgress.value = null }, ) { Text(stringResource(if (downloadError != null) R.string.ok else android.R.string.cancel)) diff --git a/app/src/main/java/io/nekohasekai/sfa/update/UpdateState.kt b/app/src/main/java/io/nekohasekai/sfa/update/UpdateState.kt index 17efa3d..19ba94e 100644 --- a/app/src/main/java/io/nekohasekai/sfa/update/UpdateState.kt +++ b/app/src/main/java/io/nekohasekai/sfa/update/UpdateState.kt @@ -11,6 +11,7 @@ object UpdateState { val isChecking = mutableStateOf(false) val isDownloading = mutableStateOf(false) + val downloadProgress = mutableStateOf(null) val downloadError = mutableStateOf(null) val cachedApkFile = mutableStateOf(null) @@ -38,6 +39,7 @@ object UpdateState { hasUpdate.value = false updateInfo.value = null isDownloading.value = false + downloadProgress.value = null downloadError.value = null installStatus.value = InstallStatus.Idle cachedApkFile.value = null @@ -46,6 +48,7 @@ object UpdateState { fun resetDownload() { isDownloading.value = false + downloadProgress.value = null downloadError.value = null }