1+ package eu.darken.butler.common.progress
2+
3+ import android.content.Context
4+ import android.graphics.drawable.Drawable
5+ import androidx.annotation.StringRes
6+ import eu.darken.butler.common.ca.CaDrawable
7+ import eu.darken.butler.common.ca.CaString
8+ import eu.darken.butler.common.ca.caString
9+ import eu.darken.butler.common.ca.toCaDrawable
10+ import eu.darken.butler.common.ca.toCaString
11+ import eu.darken.butler.common.debug.logging.Logging.Priority.*
12+ import eu.darken.butler.common.debug.logging.log
13+ import kotlinx.coroutines.CoroutineScope
14+ import kotlinx.coroutines.cancel
15+ import kotlinx.coroutines.cancelAndJoin
16+ import kotlinx.coroutines.currentCoroutineContext
17+ import kotlinx.coroutines.flow.launchIn
18+ import kotlinx.coroutines.flow.onCompletion
19+ import kotlinx.coroutines.flow.onEach
20+ import kotlinx.coroutines.isActive
21+ import kotlin.coroutines.EmptyCoroutineContext
22+
23+ fun <T : Progress .Client > T.updateProgressIcon (icon : Drawable ) {
24+ updateProgress { (it ? : Progress .Data ()).copy(icon = icon.toCaDrawable()) }
25+ }
26+
27+ fun <T : Progress .Client > T.updateProgressIcon (icon : CaDrawable ? ) {
28+ updateProgress { (it ? : Progress .Data ()).copy(icon = icon) }
29+ }
30+
31+ fun <T : Progress .Client > T.updateProgressIcon (resolv : (Context ) -> Drawable ) {
32+ updateProgress { (it ? : Progress .Data ()).copy(icon = resolv.toCaDrawable()) }
33+ }
34+
35+ fun <T : Progress .Client > T.updateProgressPrimary (primary : String ) {
36+ updateProgress { (it ? : Progress .Data ()).copy(primary = primary.toCaString()) }
37+ }
38+
39+ fun <T : Progress .Client > T.updateProgressPrimary (primary : CaString ) {
40+ updateProgress { (it ? : Progress .Data ()).copy(primary = primary) }
41+ }
42+
43+ fun <T : Progress .Client > T.updateProgressPrimary (resolv : (Context ) -> String ) {
44+ updateProgress { (it ? : Progress .Data ()).copy(primary = caString { resolv(this ) }) }
45+ }
46+
47+ fun <T : Progress .Client > T.updateProgressPrimary (@StringRes primary : Int , vararg args : Any ) {
48+ updateProgress { (it ? : Progress .Data ()).copy(primary = (primary to args).toCaString()) }
49+ }
50+
51+ fun <T : Progress .Client > T.updateProgressSecondary (secondary : String ) {
52+ updateProgress { (it ? : Progress .Data ()).copy(secondary = secondary.toCaString()) }
53+ }
54+
55+ fun <T : Progress .Client > T.updateProgressSecondary (resolv : (Context ) -> String ) {
56+ updateProgress { (it ? : Progress .Data ()).copy(secondary = caString { resolv(this ) }) }
57+ }
58+
59+ fun <T : Progress .Client > T.updateProgressSecondary (secondary : CaString = CaString .EMPTY ) {
60+ updateProgress { (it ? : Progress .Data ()).copy(secondary = secondary) }
61+ }
62+
63+ fun <T : Progress .Client > T.updateProgressSecondary (@StringRes secondary : Int , vararg args : Any ) {
64+ updateProgress { (it ? : Progress .Data ()).copy(secondary = (secondary to args).toCaString()) }
65+ }
66+
67+ fun <T : Progress .Client > T.updateProgressCount (count : Progress .Count ) {
68+ updateProgress { (it ? : Progress .Data ()).copy(count = count) }
69+ }
70+
71+ fun <T : Progress .Client > T.increaseProgress (value : Int = 1) {
72+ updateProgress {
73+ when (it?.count) {
74+ is Progress .Count .Counter -> it.copy(count = it.count.increment(value))
75+ is Progress .Count .Percent -> it.copy(count = it.count.increment(value))
76+ else -> {
77+ log(ERROR ) { " Can't increaseProgress() on type: ${it?.count} " }
78+ it
79+ }
80+ }
81+ }
82+ }
83+
84+ suspend fun <T : Progress .Host > T.forwardProgressTo (
85+ client : Progress .Client ,
86+ onUpdate : (existing: Progress .Data ? , new: Progress .Data ? ) -> Progress .Data ? ,
87+ onCompletion : (Progress .Data ? ) -> Progress .Data ? ,
88+ ) = progress
89+ .onEach { new -> client.updateProgress { onUpdate(it, new) } }
90+ .onCompletion { if (currentCoroutineContext().isActive) client.updateProgress { onCompletion(it) } }
91+
92+ suspend fun <T : Progress .Host , R > T.withProgress (
93+ client : Progress .Client ,
94+ onUpdate : (existing: Progress .Data ? , new: Progress .Data ? ) -> Progress .Data ? = { _, new -> new },
95+ onCompletion : (Progress .Data ? ) -> Progress .Data ? = { Progress .Data () },
96+ action : suspend T .() -> R
97+ ): R {
98+ val scope = CoroutineScope (EmptyCoroutineContext )
99+
100+ val forwardingJob = forwardProgressTo(
101+ client,
102+ onUpdate,
103+ onCompletion
104+ ).launchIn(scope)
105+
106+ return try {
107+ action()
108+ } finally {
109+ forwardingJob.cancelAndJoin()
110+ scope.cancel(" Finished scope" )
111+ }
112+ }
0 commit comments