mirror of
https://github.com/amir1376/ab-download-manager.git
synced 2025-02-20 11:43:24 +08:00
Merge pull request #206 from amir1376/feature/download-completion-page
add completed download page
This commit is contained in:
commit
d95511accb
@ -0,0 +1,157 @@
|
||||
package com.abdownloadmanager.desktop.pages.singleDownloadPage
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.onClick
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
||||
import com.abdownloadmanager.desktop.ui.theme.myColors
|
||||
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
||||
import com.abdownloadmanager.desktop.ui.widget.ActionButton
|
||||
import com.abdownloadmanager.desktop.ui.widget.Text
|
||||
import com.abdownloadmanager.desktop.utils.convertSizeToHumanReadable
|
||||
import com.abdownloadmanager.desktop.utils.div
|
||||
import com.abdownloadmanager.resources.Res
|
||||
import com.abdownloadmanager.utils.compose.WithContentColor
|
||||
import com.abdownloadmanager.utils.compose.widget.MyIcon
|
||||
import ir.amirab.downloader.monitor.CompletedDownloadItemState
|
||||
import ir.amirab.util.compose.resources.myStringResource
|
||||
|
||||
@Composable
|
||||
fun CompletedDownloadPage(
|
||||
component: SingleDownloadComponent,
|
||||
completedDownloadItemState: CompletedDownloadItemState,
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp
|
||||
)
|
||||
) {
|
||||
RenderFileIconAndSize(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
component = component,
|
||||
itemState = completedDownloadItemState,
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
RenderName(
|
||||
Modifier.weight(1f),
|
||||
completedDownloadItemState.name,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
Actions(Modifier, component)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Actions(
|
||||
modifier: Modifier,
|
||||
component: SingleDownloadComponent,
|
||||
) {
|
||||
Column(modifier) {
|
||||
Spacer(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(myColors.onBackground / 0.15f)
|
||||
)
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(myColors.surface / 0.5f)
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
ActionButton(
|
||||
myStringResource(Res.string.open),
|
||||
modifier = Modifier,
|
||||
onClick = {
|
||||
component.openFile()
|
||||
},
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
ActionButton(
|
||||
myStringResource(Res.string.open_folder),
|
||||
modifier = Modifier,
|
||||
onClick = {
|
||||
component.openFolder()
|
||||
},
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
ActionButton(
|
||||
myStringResource(Res.string.close),
|
||||
modifier = Modifier,
|
||||
onClick = component::close,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderName(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
WithContentColor(
|
||||
myColors.success
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MyIcon(
|
||||
MyIcons.check, null,
|
||||
Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
myStringResource(Res.string.download_page_download_completed),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = myTextSizes.lg,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
name,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.basicMarquee()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderFileIconAndSize(
|
||||
modifier: Modifier,
|
||||
component: SingleDownloadComponent,
|
||||
itemState: CompletedDownloadItemState,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
MyIcon(
|
||||
icon = component.fileIconProvider.rememberIcon(itemState.name),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = convertSizeToHumanReadable(itemState.contentLength)
|
||||
.rememberString(),
|
||||
)
|
||||
}
|
||||
}
|
@ -10,16 +10,16 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.FrameWindowScope
|
||||
import androidx.compose.ui.window.WindowPosition
|
||||
import androidx.compose.ui.window.WindowState
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.abdownloadmanager.resources.Res
|
||||
import ir.amirab.downloader.downloaditem.DownloadJobStatus
|
||||
import ir.amirab.downloader.monitor.CompletedDownloadItemState
|
||||
import ir.amirab.downloader.monitor.IDownloadItemState
|
||||
import ir.amirab.downloader.monitor.ProcessingDownloadItemState
|
||||
import ir.amirab.downloader.monitor.statusOrFinished
|
||||
import ir.amirab.downloader.utils.ExceptionUtils
|
||||
import ir.amirab.util.compose.resources.myStringResource
|
||||
import java.awt.Dimension
|
||||
import java.awt.Taskbar
|
||||
import java.awt.Window
|
||||
@ -35,20 +35,21 @@ import java.awt.Window
|
||||
// }
|
||||
//}
|
||||
@Composable
|
||||
fun getDownloadTitle(itemState: IDownloadItemState): String {
|
||||
private fun getDownloadTitle(itemState: IDownloadItemState): String {
|
||||
return buildString {
|
||||
if (itemState is ProcessingDownloadItemState && itemState.percent != null) {
|
||||
append("${itemState.percent}%")
|
||||
append("-")
|
||||
append(" ")
|
||||
}
|
||||
append("${itemState.name}")
|
||||
append(itemState.name)
|
||||
}
|
||||
}
|
||||
|
||||
val LocalSingleDownloadPageSizing = compositionLocalOf<SingleDownloadPageSizing> { error("LocalSingleBoxSizing not provided") }
|
||||
val LocalSingleDownloadPageSizing =
|
||||
compositionLocalOf<SingleProgressDownloadPageSizing> { error("LocalSingleBoxSizing not provided") }
|
||||
|
||||
@Stable
|
||||
class SingleDownloadPageSizing {
|
||||
class SingleProgressDownloadPageSizing {
|
||||
var resizingPartInfo by mutableStateOf(false)
|
||||
var partInfoHeight by mutableStateOf(150.dp)
|
||||
}
|
||||
@ -57,60 +58,145 @@ class SingleDownloadPageSizing {
|
||||
fun ShowDownloadDialogs(component: DownloadDialogManager) {
|
||||
val openedDownloadDialogs = component.openedDownloadDialogs.collectAsState().value
|
||||
for (singleDownloadComponent in openedDownloadDialogs) {
|
||||
val onRequestClose = {
|
||||
component.closeDownloadDialog(singleDownloadComponent.downloadId)
|
||||
}
|
||||
val defaultHeight = 290f
|
||||
val defaultWidth = 450f
|
||||
|
||||
val showPartInfo by singleDownloadComponent.showPartInfo.collectAsState()
|
||||
val itemState by singleDownloadComponent.itemStateFlow.collectAsState()
|
||||
val state = rememberWindowState(
|
||||
height = defaultHeight.dp,
|
||||
width = defaultWidth.dp,
|
||||
position = WindowPosition(Alignment.Center)
|
||||
)
|
||||
CustomWindow(
|
||||
state = state,
|
||||
onRequestToggleMaximize = null,
|
||||
resizable = false,
|
||||
onCloseRequest = onRequestClose,
|
||||
) {
|
||||
HandleEffects(singleDownloadComponent) {
|
||||
when (it) {
|
||||
SingleDownloadEffects.BringToFront -> {
|
||||
state.isMinimized = false
|
||||
window.toFront()
|
||||
}
|
||||
itemState?.let {
|
||||
|
||||
when (it) {
|
||||
is CompletedDownloadItemState -> {
|
||||
CompletedWindow(
|
||||
singleDownloadComponent,
|
||||
it,
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
is ProcessingDownloadItemState -> {
|
||||
ProgressWindow(
|
||||
singleDownloadComponent = singleDownloadComponent,
|
||||
itemState = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
window.minimumSize = Dimension(defaultWidth.toInt(), defaultHeight.toInt())
|
||||
}
|
||||
val singleDownloadPageSizing = remember(showPartInfo) { SingleDownloadPageSizing() }
|
||||
WindowTitle(itemState?.let { getDownloadTitle(it) } ?: myStringResource(Res.string.download))
|
||||
WindowIcon(MyIcons.appIcon)
|
||||
var h = defaultHeight
|
||||
var w = defaultWidth
|
||||
if (showPartInfo && itemState is ProcessingDownloadItemState) {
|
||||
h += singleDownloadPageSizing.partInfoHeight.value
|
||||
}
|
||||
LaunchedEffect(w, h) {
|
||||
state.size = DpSize(
|
||||
width = w.dp,
|
||||
height = h.dp
|
||||
)
|
||||
}
|
||||
itemState?.let { itemState ->
|
||||
UpdateTaskBar(window, itemState)
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalSingleDownloadPageSizing provides singleDownloadPageSizing
|
||||
) {
|
||||
SingleDownloadPage(singleDownloadComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FrameWindowScope.CommonContent(
|
||||
singleDownloadComponent: SingleDownloadComponent,
|
||||
state: WindowState,
|
||||
itemState: IDownloadItemState,
|
||||
) {
|
||||
HandleEffects(singleDownloadComponent) {
|
||||
when (it) {
|
||||
SingleDownloadEffects.BringToFront -> {
|
||||
state.isMinimized = false
|
||||
window.toFront()
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowTitle(getDownloadTitle(itemState))
|
||||
WindowIcon(MyIcons.appIcon)
|
||||
UpdateTaskBar(window, itemState)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CompletedWindow(
|
||||
singleDownloadComponent: SingleDownloadComponent,
|
||||
itemState: CompletedDownloadItemState,
|
||||
) {
|
||||
val onRequestClose = {
|
||||
singleDownloadComponent.close()
|
||||
}
|
||||
val defaultHeight = 160f
|
||||
val defaultWidth = 450f
|
||||
val state = rememberWindowState(
|
||||
height = defaultHeight.dp,
|
||||
width = defaultWidth.dp,
|
||||
position = WindowPosition(Alignment.Center)
|
||||
)
|
||||
CustomWindow(
|
||||
state = state,
|
||||
onRequestToggleMaximize = null,
|
||||
resizable = false,
|
||||
alwaysOnTop = true,
|
||||
onCloseRequest = onRequestClose,
|
||||
) {
|
||||
CommonContent(
|
||||
singleDownloadComponent = singleDownloadComponent,
|
||||
state = state,
|
||||
itemState = itemState,
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
window.minimumSize = Dimension(defaultWidth.toInt(), defaultHeight.toInt())
|
||||
}
|
||||
var h = defaultHeight
|
||||
var w = defaultWidth
|
||||
LaunchedEffect(w, h) {
|
||||
state.size = DpSize(
|
||||
width = w.dp,
|
||||
height = h.dp
|
||||
)
|
||||
}
|
||||
CompletedDownloadPage(
|
||||
singleDownloadComponent,
|
||||
itemState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProgressWindow(
|
||||
singleDownloadComponent: SingleDownloadComponent,
|
||||
itemState: ProcessingDownloadItemState,
|
||||
) {
|
||||
val onRequestClose = {
|
||||
singleDownloadComponent.close()
|
||||
}
|
||||
val defaultHeight = 290f
|
||||
val defaultWidth = 450f
|
||||
|
||||
val showPartInfo by singleDownloadComponent.showPartInfo.collectAsState()
|
||||
val state = rememberWindowState(
|
||||
height = defaultHeight.dp,
|
||||
width = defaultWidth.dp,
|
||||
position = WindowPosition(Alignment.Center)
|
||||
)
|
||||
CustomWindow(
|
||||
state = state,
|
||||
onRequestToggleMaximize = null,
|
||||
resizable = false,
|
||||
onCloseRequest = onRequestClose,
|
||||
) {
|
||||
CommonContent(
|
||||
singleDownloadComponent = singleDownloadComponent,
|
||||
state = state,
|
||||
itemState = itemState,
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
window.minimumSize = Dimension(defaultWidth.toInt(), defaultHeight.toInt())
|
||||
}
|
||||
val singleDownloadPageSizing = remember(showPartInfo) { SingleProgressDownloadPageSizing() }
|
||||
var h = defaultHeight
|
||||
var w = defaultWidth
|
||||
if (showPartInfo) {
|
||||
h += singleDownloadPageSizing.partInfoHeight.value
|
||||
}
|
||||
LaunchedEffect(w, h) {
|
||||
state.size = DpSize(
|
||||
width = w.dp,
|
||||
height = h.dp
|
||||
)
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalSingleDownloadPageSizing provides singleDownloadPageSizing
|
||||
) {
|
||||
ProgressDownloadPage(
|
||||
singleDownloadComponent,
|
||||
itemState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -65,80 +65,77 @@ enum class SingleDownloadPageSections(
|
||||
private val tabs = SingleDownloadPageSections.entries.toList()
|
||||
|
||||
@Composable
|
||||
fun SingleDownloadPage(singleDownloadComponent: SingleDownloadComponent) {
|
||||
val itemState = singleDownloadComponent.itemStateFlow.collectAsState().value
|
||||
fun ProgressDownloadPage(singleDownloadComponent: SingleDownloadComponent, itemState: ProcessingDownloadItemState) {
|
||||
var selectedTab by remember { mutableStateOf(Info) }
|
||||
val showPartInfo by singleDownloadComponent.showPartInfo.collectAsState()
|
||||
val setShowPartInfo = singleDownloadComponent::setShowPartInfo
|
||||
if (itemState != null) {
|
||||
Column(
|
||||
Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Column(
|
||||
Modifier.padding(horizontal = 16.dp)
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(myColors.surface)
|
||||
.padding(1.dp),
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(myColors.surface)
|
||||
.padding(1.dp),
|
||||
) {
|
||||
//tabs
|
||||
MyTabRow {
|
||||
for (tab in tabs) {
|
||||
MyTab(
|
||||
selected = tab == selectedTab, {
|
||||
selectedTab = tab
|
||||
},
|
||||
icon = tab.icon,
|
||||
title = tab.title
|
||||
)
|
||||
}
|
||||
}
|
||||
val scrollState = rememberScrollState()
|
||||
//info / settings ...
|
||||
val tabContentModifier = Modifier
|
||||
|
||||
Box(
|
||||
Modifier.height(150.dp)
|
||||
.clip(RoundedCornerShape(bottomStart = 6.dp, bottomEnd = 6.dp))
|
||||
.background(myColors.background)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
when (selectedTab) {
|
||||
Info -> RenderInfo(
|
||||
tabContentModifier,
|
||||
singleDownloadComponent
|
||||
)
|
||||
|
||||
Settings -> RenderSettings(
|
||||
tabContentModifier.padding(end = 12.dp),
|
||||
singleDownloadComponent,
|
||||
)
|
||||
}
|
||||
VerticalScrollbar(
|
||||
adapter = rememberScrollbarAdapter(scrollState),
|
||||
modifier = Modifier.matchParentSize().wrapContentWidth(Alignment.End),
|
||||
style = LocalScrollbarStyle.current.copy(
|
||||
shape = RectangleShape
|
||||
)
|
||||
//tabs
|
||||
MyTabRow {
|
||||
for (tab in tabs) {
|
||||
MyTab(
|
||||
selected = tab == selectedTab, {
|
||||
selectedTab = tab
|
||||
},
|
||||
icon = tab.icon,
|
||||
title = tab.title
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Column(Modifier) {
|
||||
RenderProgressBar(itemState)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
RenderActions(itemState, singleDownloadComponent, showPartInfo, setShowPartInfo)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
}
|
||||
val resizingState = LocalSingleDownloadPageSizing.current
|
||||
LaunchedEffect(resizingState.resizingPartInfo) {
|
||||
if (resizingState.partInfoHeight <= 0.dp) {
|
||||
setShowPartInfo(false)
|
||||
val scrollState = rememberScrollState()
|
||||
//info / settings ...
|
||||
val tabContentModifier = Modifier
|
||||
|
||||
Box(
|
||||
Modifier.height(150.dp)
|
||||
.clip(RoundedCornerShape(bottomStart = 6.dp, bottomEnd = 6.dp))
|
||||
.background(myColors.background)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
when (selectedTab) {
|
||||
Info -> RenderInfo(
|
||||
tabContentModifier,
|
||||
singleDownloadComponent
|
||||
)
|
||||
|
||||
Settings -> RenderSettings(
|
||||
tabContentModifier.padding(end = 12.dp),
|
||||
singleDownloadComponent,
|
||||
)
|
||||
}
|
||||
VerticalScrollbar(
|
||||
adapter = rememberScrollbarAdapter(scrollState),
|
||||
modifier = Modifier.matchParentSize().wrapContentWidth(Alignment.End),
|
||||
style = LocalScrollbarStyle.current.copy(
|
||||
shape = RectangleShape
|
||||
)
|
||||
)
|
||||
}
|
||||
if (showPartInfo && itemState is ProcessingDownloadItemState) {
|
||||
RenderPartInfo(itemState)
|
||||
}
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Column(Modifier) {
|
||||
RenderProgressBar(itemState)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
RenderActions(itemState, singleDownloadComponent, showPartInfo, setShowPartInfo)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
}
|
||||
val resizingState = LocalSingleDownloadPageSizing.current
|
||||
LaunchedEffect(resizingState.resizingPartInfo) {
|
||||
if (resizingState.partInfoHeight <= 0.dp) {
|
||||
setShowPartInfo(false)
|
||||
}
|
||||
}
|
||||
if (showPartInfo) {
|
||||
RenderPartInfo(itemState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,7 +482,7 @@ fun RenderInfo(
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 8.dp)
|
||||
) {
|
||||
for (propertyItem in singleDownloadComponent.extraDownloadInfo.collectAsState().value) {
|
||||
for (propertyItem in singleDownloadComponent.extraDownloadProgressInfo.collectAsState().value) {
|
||||
Spacer(Modifier.height(2.dp))
|
||||
RenderPropertyItem(propertyItem)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import com.abdownloadmanager.desktop.utils.mvi.supportEffects
|
||||
import arrow.optics.copy
|
||||
import com.abdownloadmanager.desktop.storage.PageStatesStorage
|
||||
import com.abdownloadmanager.resources.Res
|
||||
import com.abdownloadmanager.resources.*
|
||||
import com.abdownloadmanager.utils.FileIconProvider
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import ir.amirab.downloader.DownloadManagerEvents
|
||||
import ir.amirab.downloader.downloaditem.DownloadJobStatus
|
||||
@ -19,6 +19,7 @@ import ir.amirab.downloader.monitor.*
|
||||
import ir.amirab.util.compose.StringSource
|
||||
import ir.amirab.util.compose.asStringSource
|
||||
import ir.amirab.util.compose.asStringSourceWithARgs
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -42,20 +43,44 @@ data class SingleDownloadPagePropertyItem(
|
||||
class SingleDownloadComponent(
|
||||
ctx: ComponentContext,
|
||||
val downloadItemOpener: DownloadItemOpener,
|
||||
val onDismiss: () -> Unit,
|
||||
private val onDismiss: () -> Unit,
|
||||
val downloadId: Long,
|
||||
) : BaseComponent(ctx),
|
||||
ContainsEffects<SingleDownloadEffects> by supportEffects(),
|
||||
KoinComponent {
|
||||
private val downloadSystem: DownloadSystem by inject()
|
||||
val fileIconProvider: FileIconProvider by inject()
|
||||
private val singleDownloadPageStateToPersist by lazy {
|
||||
get<PageStatesStorage>().downloadPage
|
||||
}
|
||||
private val downloadMonitor: IDownloadMonitor = downloadSystem.downloadMonitor
|
||||
private val downloadManager: DownloadManager = downloadSystem.downloadManager
|
||||
val itemStateFlow = downloadMonitor.downloadListFlow.map {
|
||||
it.firstOrNull { it.id == downloadId }
|
||||
}.stateIn(scope, SharingStarted.Eagerly, null)
|
||||
|
||||
val itemStateFlow = MutableStateFlow<IDownloadItemState?>(null)
|
||||
private fun shouldShowCompletionDialog(): Boolean {
|
||||
// TODO implement an option to allow user disable this
|
||||
return true
|
||||
}
|
||||
|
||||
init {
|
||||
downloadMonitor
|
||||
.downloadListFlow
|
||||
.conflate()
|
||||
.onEach {
|
||||
val item = it.firstOrNull { it.id == downloadId }
|
||||
val previous = itemStateFlow.value
|
||||
if (previous is ProcessingDownloadItemState && item is CompletedDownloadItemState) {
|
||||
// if It was opened to show progress
|
||||
if (shouldShowCompletionDialog()) {
|
||||
itemStateFlow.value = item
|
||||
} else {
|
||||
close()
|
||||
}
|
||||
} else {
|
||||
itemStateFlow.value = item
|
||||
}
|
||||
}.launchIn(scope)
|
||||
}
|
||||
|
||||
private val _showPartInfo = MutableStateFlow(singleDownloadPageStateToPersist.value.showPartInfo)
|
||||
val showPartInfo = _showPartInfo.asStateFlow()
|
||||
@ -68,8 +93,9 @@ class SingleDownloadComponent(
|
||||
}
|
||||
}
|
||||
|
||||
val extraDownloadInfo: StateFlow<List<SingleDownloadPagePropertyItem>> = itemStateFlow
|
||||
.filterNotNull()
|
||||
// TODO this can be moved to a nested component to reduce system resource usage
|
||||
val extraDownloadProgressInfo: StateFlow<List<SingleDownloadPagePropertyItem>> = itemStateFlow
|
||||
.filterIsInstance<ProcessingDownloadItemState>()
|
||||
.map {
|
||||
buildList {
|
||||
add(SingleDownloadPagePropertyItem(Res.string.name.asStringSource(), it.name.asStringSource()))
|
||||
@ -80,48 +106,41 @@ class SingleDownloadComponent(
|
||||
convertSizeToHumanReadable(it.contentLength)
|
||||
)
|
||||
)
|
||||
when (it) {
|
||||
is CompletedDownloadItemState -> {
|
||||
}
|
||||
|
||||
is ProcessingDownloadItemState -> {
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.download_page_downloaded_size.asStringSource(),
|
||||
convertBytesToHumanReadable(it.progress).orEmpty().asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.speed.asStringSource(),
|
||||
convertSpeedToHumanReadable(it.speed).asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.time_left.asStringSource(),
|
||||
(it.remainingTime?.let { remainingTime ->
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.download_page_downloaded_size.asStringSource(),
|
||||
convertBytesToHumanReadable(it.progress).orEmpty().asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.speed.asStringSource(),
|
||||
convertSpeedToHumanReadable(it.speed).asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.time_left.asStringSource(),
|
||||
(it.remainingTime?.let { remainingTime ->
|
||||
convertTimeRemainingToHumanReadable(remainingTime, TimeNames.ShortNames)
|
||||
}.orEmpty()).asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.resume_support.asStringSource(),
|
||||
when (it.supportResume) {
|
||||
true -> Res.string.yes.asStringSource()
|
||||
false -> Res.string.no.asStringSource()
|
||||
null -> Res.string.unknown.asStringSource()
|
||||
},
|
||||
when (it.supportResume) {
|
||||
true -> SingleDownloadPagePropertyItem.ValueType.Success
|
||||
false -> SingleDownloadPagePropertyItem.ValueType.Error
|
||||
null -> SingleDownloadPagePropertyItem.ValueType.Normal
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}.orEmpty()).asStringSource()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleDownloadPagePropertyItem(
|
||||
Res.string.resume_support.asStringSource(),
|
||||
when (it.supportResume) {
|
||||
true -> Res.string.yes.asStringSource()
|
||||
false -> Res.string.no.asStringSource()
|
||||
null -> Res.string.unknown.asStringSource()
|
||||
},
|
||||
when (it.supportResume) {
|
||||
true -> SingleDownloadPagePropertyItem.ValueType.Success
|
||||
false -> SingleDownloadPagePropertyItem.ValueType.Error
|
||||
null -> SingleDownloadPagePropertyItem.ValueType.Normal
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
@ -154,7 +173,7 @@ class SingleDownloadComponent(
|
||||
}
|
||||
}
|
||||
|
||||
fun openFile() {
|
||||
fun openFile(alsoClose: Boolean = true) {
|
||||
val itemState = itemStateFlow.value
|
||||
scope.launch {
|
||||
if (itemState is CompletedDownloadItemState) {
|
||||
@ -162,12 +181,14 @@ class SingleDownloadComponent(
|
||||
downloadItemOpener.openDownloadItem(downloadId)
|
||||
}
|
||||
}
|
||||
onDismiss()
|
||||
if (alsoClose) {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toggle() {
|
||||
val state = itemStateFlow.value as ProcessingDownloadItemState ?: return
|
||||
val state = itemStateFlow.value as? ProcessingDownloadItemState ?: return
|
||||
scope.launch {
|
||||
if (state.status is DownloadJobStatus.IsActive) {
|
||||
downloadSystem.manualPause(downloadId)
|
||||
@ -178,7 +199,7 @@ class SingleDownloadComponent(
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
val state = itemStateFlow.value as ProcessingDownloadItemState ?: return
|
||||
val state = itemStateFlow.value as? ProcessingDownloadItemState ?: return
|
||||
scope.launch {
|
||||
if (state.status is DownloadJobStatus.CanBeResumed) {
|
||||
downloadSystem.manualResume(downloadId)
|
||||
@ -187,7 +208,7 @@ class SingleDownloadComponent(
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
val state = itemStateFlow.value as ProcessingDownloadItemState ?: return
|
||||
val state = itemStateFlow.value as? ProcessingDownloadItemState ?: return
|
||||
scope.launch {
|
||||
if (state.status is DownloadJobStatus.IsActive) {
|
||||
downloadSystem.manualPause(downloadId)
|
||||
|
@ -54,7 +54,7 @@ val darkColors = MyColors(
|
||||
surface = Color(0xFF22222A),
|
||||
error = Color(0xffff5757),
|
||||
onError = Color.White,
|
||||
success = Color(0xff14a600),
|
||||
success = Color(0xff69BA5A),
|
||||
onSuccess = Color.White,
|
||||
warning = Color(0xFFffbe56),
|
||||
onWarning = Color.White,
|
||||
|
@ -254,6 +254,7 @@ time_left=Time Left
|
||||
date_added=Date Added
|
||||
info=Info
|
||||
download_page_downloaded_size=Downloaded
|
||||
download_page_download_completed=Download Completed
|
||||
resume_support=Resume Support
|
||||
yes=Yes
|
||||
no=No
|
||||
|
Loading…
x
Reference in New Issue
Block a user