Skip to content

Commit 4b820e5

Browse files
committed
feat: live time elapsed display on install steps
1 parent bd5bc39 commit 4b820e5

File tree

7 files changed

+105
-22
lines changed

7 files changed

+105
-22
lines changed

app/src/main/kotlin/com/aliucord/manager/installer/steps/StepRunner.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ abstract class StepRunner : KoinComponent {
5353
}
5454

5555
// Skip minimum run time when in dev mode
56-
if (!preferences.devMode && step.durationMs < MINIMUM_STEP_DELAY) {
57-
delay(MINIMUM_STEP_DELAY - step.durationMs)
56+
val duration = step.getDuration()
57+
if (!preferences.devMode && duration < MINIMUM_STEP_DELAY) {
58+
delay(MINIMUM_STEP_DELAY - duration)
5859
}
5960
}
6061

app/src/main/kotlin/com/aliucord/manager/installer/steps/base/Step.kt

+43-8
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import androidx.annotation.StringRes
44
import androidx.compose.runtime.*
55
import com.aliucord.manager.installer.steps.StepGroup
66
import com.aliucord.manager.installer.steps.StepRunner
7-
import kotlinx.coroutines.Dispatchers
8-
import kotlinx.coroutines.withContext
7+
import com.aliucord.manager.util.toPrecision
8+
import kotlinx.coroutines.*
99
import org.koin.core.time.measureTimedValue
10-
import kotlin.math.roundToInt
10+
import kotlin.math.roundToLong
1111

1212
/**
1313
* A base install process step. Steps are single-use
@@ -44,12 +44,44 @@ abstract class Step {
4444
var progress by mutableFloatStateOf(-1f)
4545
protected set
4646

47+
private val durationSecs = mutableFloatStateOf(0f)
48+
private var startTime: Long? = null
49+
private var totalTimeMs: Long? = null
50+
51+
/**
52+
* The total amount of time this step has/was executed for in milliseconds.
53+
* If this step has not started executing then it will return `0`.
54+
*/
55+
fun getDuration(): Long {
56+
// Step hasn't started executing
57+
val startTime = startTime ?: return 0
58+
59+
// Step already finished executing
60+
totalTimeMs?.let { return it }
61+
62+
return System.currentTimeMillis() - startTime
63+
}
64+
4765
/**
48-
* The total execution time once this step has finished execution.
66+
* The live execution time of this step in seconds.
67+
* The value is clamped to a resolution of 10ms updated every 50ms.
4968
*/
50-
// TODO: make this a live value
51-
var durationMs by mutableIntStateOf(0)
52-
private set
69+
@Composable
70+
fun collectDurationAsState(): State<Float> {
71+
if (state.isFinished)
72+
return durationSecs
73+
74+
LaunchedEffect(state) {
75+
while (true) {
76+
durationSecs.floatValue = (getDuration() / 1000.0)
77+
.toPrecision(2).toFloat()
78+
79+
delay(50)
80+
}
81+
}
82+
83+
return durationSecs
84+
}
5385

5486
/**
5587
* Thin wrapper over [execute] but handling errors.
@@ -60,6 +92,7 @@ abstract class Step {
6092
throw IllegalStateException("Cannot execute a step that has already started")
6193

6294
state = StepState.Running
95+
startTime = System.currentTimeMillis()
6396

6497
// Execute this steps logic while timing it
6598
val (error, executionTimeMs) = measureTimedValue {
@@ -78,7 +111,9 @@ abstract class Step {
78111
}
79112
}
80113

81-
durationMs = executionTimeMs.roundToInt()
114+
totalTimeMs = executionTimeMs.roundToLong()
115+
durationSecs.floatValue = executionTimeMs.toFloat() / 1000f
116+
82117
return error
83118
}
84119
}

app/src/main/kotlin/com/aliucord/manager/installer/steps/base/StepState.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ enum class StepState {
88
Skipped;
99

1010
val isFinished: Boolean
11-
get() = this == Success || this == Error || this == Skipped
11+
get() = this == Success || this == Skipped || this == Error
1212
}

app/src/main/kotlin/com/aliucord/manager/ui/screens/install/components/StepGroupCard.kt

+13-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ fun StepGroupCard(
4141
}
4242
}
4343

44+
val totalSeconds = remember(groupState.isFinished) {
45+
if (!groupState.isFinished) {
46+
0f
47+
} else {
48+
subSteps
49+
.sumOf { step -> step.getDuration() }
50+
.div(1000f)
51+
}
52+
}
53+
4454
LaunchedEffect(groupState) {
4555
if (groupState != StepState.Pending)
4656
onExpand()
@@ -68,9 +78,9 @@ fun StepGroupCard(
6878

6979
Spacer(modifier = Modifier.weight(1f))
7080

71-
if (groupState.isFinished) Text(
72-
"%.2fs".format(subSteps.sumOf { it.durationMs } / 1000f),
73-
style = MaterialTheme.typography.labelMedium
81+
TimeElapsed(
82+
enabled = groupState.isFinished,
83+
seconds = totalSeconds,
7484
)
7585

7686
if (isExpanded) {

app/src/main/kotlin/com/aliucord/manager/ui/screens/install/components/StepItem.kt

+5-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.ui.res.stringResource
1111
import androidx.compose.ui.text.style.TextOverflow
1212
import androidx.compose.ui.unit.dp
1313
import com.aliucord.manager.installer.steps.base.Step
14+
import com.aliucord.manager.installer.steps.base.StepState
1415

1516
@Composable
1617
fun StepItem(
@@ -36,13 +37,9 @@ fun StepItem(
3637
modifier = Modifier.weight(1f, true),
3738
)
3839

39-
// TODO: live step duration counter
40-
if (step.state.isFinished) {
41-
Text(
42-
text = "%.2fs".format(step.durationMs / 1000f),
43-
style = MaterialTheme.typography.labelSmall,
44-
maxLines = 1,
45-
)
46-
}
40+
TimeElapsed(
41+
enabled = step.state != StepState.Pending,
42+
seconds = step.collectDurationAsState().value,
43+
)
4744
}
4845
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.aliucord.manager.ui.screens.install.components
2+
3+
import androidx.compose.animation.*
4+
import androidx.compose.material3.MaterialTheme
5+
import androidx.compose.material3.Text
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
9+
@Composable
10+
fun TimeElapsed(
11+
seconds: Float,
12+
enabled: Boolean = true,
13+
modifier: Modifier = Modifier,
14+
) {
15+
AnimatedVisibility(
16+
visible = enabled,
17+
enter = fadeIn(),
18+
exit = ExitTransition.None,
19+
label = "TimeElapsed Visibility"
20+
) {
21+
Text(
22+
text = "%.2fs".format(seconds),
23+
style = MaterialTheme.typography.labelMedium,
24+
maxLines = 1,
25+
modifier = modifier,
26+
)
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.aliucord.manager.util
2+
3+
import kotlin.math.pow
4+
import kotlin.math.truncate
5+
6+
/**
7+
* Truncates this value to a specific number of [decimals] digits.
8+
*/
9+
fun Double.toPrecision(decimals: Int): Double {
10+
val multiplier = 10.0.pow(decimals)
11+
return truncate(this * multiplier) / multiplier
12+
}

0 commit comments

Comments
 (0)