From 8b5adfd6b73b7f0ea9f97b53392a46782b1088ed Mon Sep 17 00:00:00 2001 From: acite <1498045907@qq.com> Date: Mon, 29 Sep 2025 20:34:56 +0800 Subject: [PATCH] [feat] Better Transmission UI --- .../acitelight/aether/service/ApiClient.kt | 6 +- .../acitelight/aether/service/FetchManager.kt | 4 + .../com/acitelight/aether/view/BiliStyle.kt | 20 +- .../aether/view/TransmissionScreen.kt | 251 ++-------------- .../aether/view/VideoDownloadCard.kt | 274 ++++++++++++++++++ .../aether/view/VideoDownloadCardMini.kt | 240 +++++++++++++++ .../viewModel/TransmissionScreenViewModel.kt | 61 +++- .../aether/viewModel/VideoScreenViewModel.kt | 5 +- 8 files changed, 614 insertions(+), 247 deletions(-) create mode 100644 app/src/main/java/com/acitelight/aether/view/VideoDownloadCard.kt create mode 100644 app/src/main/java/com/acitelight/aether/view/VideoDownloadCardMini.kt diff --git a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt index fc109a5..e4e97bf 100644 --- a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt +++ b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt @@ -231,6 +231,7 @@ class ApiClient @Inject constructor( } if (selectedUrl == null) { + client = createOkHttp() throw Exception("No reachable URL found") } @@ -242,11 +243,6 @@ class ApiClient @Inject constructor( (context as AetherApp).abyssService?.proxy?.config(getBase().toUri().host!!, 4096) } api = createRetrofit().create(ApiInterface::class.java) - - Log.i("Delay Analyze", "Start Abyss Hello") - val h = api!!.hello() - Log.i("Delay Analyze", "Abyss Hello: ${h.string()}") - return base } catch (_: Exception) { api = null diff --git a/app/src/main/java/com/acitelight/aether/service/FetchManager.kt b/app/src/main/java/com/acitelight/aether/service/FetchManager.kt index c9f7150..2d12ff6 100644 --- a/app/src/main/java/com/acitelight/aether/service/FetchManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/FetchManager.kt @@ -77,6 +77,10 @@ class FetchManager @Inject constructor( fetch?.resume(id) } + fun retry(id: Int) { + fetch?.retry(id) + } + fun cancel(id: Int) { fetch?.cancel(id) } diff --git a/app/src/main/java/com/acitelight/aether/view/BiliStyle.kt b/app/src/main/java/com/acitelight/aether/view/BiliStyle.kt index 0a4df45..36d42d4 100644 --- a/app/src/main/java/com/acitelight/aether/view/BiliStyle.kt +++ b/app/src/main/java/com/acitelight/aether/view/BiliStyle.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider +import androidx.compose.material3.SliderColors import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -63,9 +64,13 @@ fun BiliMiniSlider( modifier: Modifier = Modifier, value: Float, onValueChange: (Float) -> Unit, - valueRange: ClosedFloatingPointRange = 0f..1f + valueRange: ClosedFloatingPointRange = 0f..1f, + colors: SliderColors = SliderDefaults.colors( + thumbColor = Color(0xFFFFFFFF), + activeTrackColor = MaterialTheme.colorScheme.primary, + inactiveTrackColor = Color.LightGray.copy(alpha = 0.4f) + ) ) { - val colorScheme = MaterialTheme.colorScheme val trackHeight = 3.dp Slider( @@ -73,11 +78,8 @@ fun BiliMiniSlider( onValueChange = onValueChange, valueRange = valueRange, modifier = modifier, - colors = SliderDefaults.colors( - thumbColor = Color(0xFFFFFFFF), - activeTrackColor = colorScheme.primary, - inactiveTrackColor = Color.LightGray.copy(alpha = 0.4f) - ), + colors = colors, + enabled = false, thumb = { }, @@ -86,14 +88,14 @@ fun BiliMiniSlider( Modifier .height(trackHeight) .fillMaxWidth() - .background(Color.LightGray.copy(alpha = 0.3f), RoundedCornerShape(50)) + .background(colors.inactiveTrackColor, RoundedCornerShape(50)) ) { Box( Modifier .align(Alignment.CenterStart) .fillMaxWidth(value) .fillMaxHeight() - .background(colorScheme.primary, RoundedCornerShape(50)) + .background(colors.activeTrackColor, RoundedCornerShape(50)) ) } } diff --git a/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt b/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt index 2c18261..b22588f 100644 --- a/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt @@ -84,15 +84,27 @@ fun TransmissionScreen( maxLines = 1 ) + val downloading = downloads.filter { it.status == Status.DOWNLOADING } + BiliMiniSlider( + value = if (downloading.sumOf { it.totalBytes } == 0L) 1f else downloading.sumOf { it.downloadedBytes } / downloading.sumOf { it.totalBytes }.toFloat(), + modifier = Modifier + .height(6.dp) + .align(Alignment.End) + .fillMaxWidth(), + onValueChange = { + + } + ) + HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color) LazyColumn( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(12.dp) ) { - items(downloads.filter { it.type == "main" }, key = { it.id }) { item -> - VideoDownloadCard( + items(downloads.filter { it.type == "main" }.sortedBy { it.status == Status.COMPLETED }, key = { it.id }) { item -> + VideoDownloadCardMini( navigator = navigator, viewModel = transmissionScreenViewModel, model = item, @@ -112,13 +124,19 @@ fun TransmissionScreen( for (i in downloadToGroup( item, downloads - )) transmissionScreenViewModel.cancel(i.id) + )) transmissionScreenViewModel.delete(i.id) }, onDelete = { for (i in downloadToGroup( item, downloads )) transmissionScreenViewModel.delete(i.id) + }, + onRetry = { + for (i in downloadToGroup( + item, + downloads + )) transmissionScreenViewModel.retry(i.id) } ) } @@ -126,231 +144,6 @@ fun TransmissionScreen( } } - -@Composable -private fun VideoDownloadCard( - navigator: NavHostController, - viewModel: TransmissionScreenViewModel, - model: VideoDownloadItemState, - onPause: () -> Unit, - onResume: () -> Unit, - onCancel: () -> Unit, - onDelete: () -> Unit -) { - Card( - shape = RoundedCornerShape(8.dp), - elevation = CardDefaults.cardElevation(4.dp), - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - .background(Color.Transparent) - .clickable(onClick = { - if (model.status == Status.COMPLETED) { - viewModel.viewModelScope.launch(Dispatchers.IO) - { - val downloaded = viewModel.fetchManager.getAllDownloadsAsync().filter { - it.status == Status.COMPLETED && it.extras.getString( - "class", - "" - ) != "comic" && it.extras.getString( - "type", - "" - ) == "main" - } - - val jsonQuery = downloaded.map { - File( - viewModel.context.getExternalFilesDir(null), - "videos/${ - it.extras.getString( - "class", - "" - ) - }/${it.extras.getString("id", "")}/summary.json" - ).readText() - } - .map { - Json.decodeFromString