Skip to content

Commit

Permalink
feat: live time elapsed display on install steps
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Mar 11, 2024
1 parent bd5bc39 commit 4b820e5
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ abstract class StepRunner : KoinComponent {
}

// Skip minimum run time when in dev mode
if (!preferences.devMode && step.durationMs < MINIMUM_STEP_DELAY) {
delay(MINIMUM_STEP_DELAY - step.durationMs)
val duration = step.getDuration()
if (!preferences.devMode && duration < MINIMUM_STEP_DELAY) {
delay(MINIMUM_STEP_DELAY - duration)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import androidx.annotation.StringRes
import androidx.compose.runtime.*
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.aliucord.manager.util.toPrecision
import kotlinx.coroutines.*
import org.koin.core.time.measureTimedValue
import kotlin.math.roundToInt
import kotlin.math.roundToLong

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

private val durationSecs = mutableFloatStateOf(0f)
private var startTime: Long? = null
private var totalTimeMs: Long? = null

/**
* The total amount of time this step has/was executed for in milliseconds.
* If this step has not started executing then it will return `0`.
*/
fun getDuration(): Long {
// Step hasn't started executing
val startTime = startTime ?: return 0

// Step already finished executing
totalTimeMs?.let { return it }

return System.currentTimeMillis() - startTime
}

/**
* The total execution time once this step has finished execution.
* The live execution time of this step in seconds.
* The value is clamped to a resolution of 10ms updated every 50ms.
*/
// TODO: make this a live value
var durationMs by mutableIntStateOf(0)
private set
@Composable
fun collectDurationAsState(): State<Float> {
if (state.isFinished)
return durationSecs

LaunchedEffect(state) {
while (true) {
durationSecs.floatValue = (getDuration() / 1000.0)
.toPrecision(2).toFloat()

delay(50)
}
}

return durationSecs
}

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

state = StepState.Running
startTime = System.currentTimeMillis()

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

durationMs = executionTimeMs.roundToInt()
totalTimeMs = executionTimeMs.roundToLong()
durationSecs.floatValue = executionTimeMs.toFloat() / 1000f

return error
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ enum class StepState {
Skipped;

val isFinished: Boolean
get() = this == Success || this == Error || this == Skipped
get() = this == Success || this == Skipped || this == Error
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ fun StepGroupCard(
}
}

val totalSeconds = remember(groupState.isFinished) {
if (!groupState.isFinished) {
0f
} else {
subSteps
.sumOf { step -> step.getDuration() }
.div(1000f)
}
}

LaunchedEffect(groupState) {
if (groupState != StepState.Pending)
onExpand()
Expand Down Expand Up @@ -68,9 +78,9 @@ fun StepGroupCard(

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

if (groupState.isFinished) Text(
"%.2fs".format(subSteps.sumOf { it.durationMs } / 1000f),
style = MaterialTheme.typography.labelMedium
TimeElapsed(
enabled = groupState.isFinished,
seconds = totalSeconds,
)

if (isExpanded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState

@Composable
fun StepItem(
Expand All @@ -36,13 +37,9 @@ fun StepItem(
modifier = Modifier.weight(1f, true),
)

// TODO: live step duration counter
if (step.state.isFinished) {
Text(
text = "%.2fs".format(step.durationMs / 1000f),
style = MaterialTheme.typography.labelSmall,
maxLines = 1,
)
}
TimeElapsed(
enabled = step.state != StepState.Pending,
seconds = step.collectDurationAsState().value,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.aliucord.manager.ui.screens.install.components

import androidx.compose.animation.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun TimeElapsed(
seconds: Float,
enabled: Boolean = true,
modifier: Modifier = Modifier,
) {
AnimatedVisibility(
visible = enabled,
enter = fadeIn(),
exit = ExitTransition.None,
label = "TimeElapsed Visibility"
) {
Text(
text = "%.2fs".format(seconds),
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
modifier = modifier,
)
}
}
12 changes: 12 additions & 0 deletions app/src/main/kotlin/com/aliucord/manager/util/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.aliucord.manager.util

import kotlin.math.pow
import kotlin.math.truncate

/**
* Truncates this value to a specific number of [decimals] digits.
*/
fun Double.toPrecision(decimals: Int): Double {
val multiplier = 10.0.pow(decimals)
return truncate(this * multiplier) / multiplier
}

0 comments on commit 4b820e5

Please sign in to comment.