Skip to content

Commit

Permalink
Add FullScreen Prefs dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
RotBolt committed Jul 26, 2023
1 parent ac3cfbe commit 4e04049
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 20 deletions.
16 changes: 8 additions & 8 deletions config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ complexity:
threshold: 160
LongParameterList:
active: true
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: false
functionThreshold: 11
constructorThreshold: 11
ignoreDefaultParameters: true
ignoreDataClasses: true
ignoreAnnotatedParameter: []
excludes: ['**/build/**']
Expand Down Expand Up @@ -168,11 +168,11 @@ complexity:
TooManyFunctions:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
thresholdInFiles: 11
thresholdInClasses: 11
thresholdInInterfaces: 11
thresholdInObjects: 11
thresholdInEnums: 11
thresholdInFiles: 21
thresholdInClasses: 21
thresholdInInterfaces: 21
thresholdInObjects: 21
thresholdInEnums: 21
ignoreDeprecated: true
ignorePrivate: true
ignoreOverridden: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -58,27 +57,42 @@ class FlakerActivity : ComponentActivity() {
FlakerBar(
state = state,
scrollBehavior = scrollBehavior,
onToggleChange = viewModel::toggleFlaker
onToggleChange = viewModel::toggleFlaker,
onPrefsClick = viewModel::openPrefs
)
}
) { scaffoldPadding ->
NetworkRequestList(modifier = Modifier.padding(scaffoldPadding), state = state)
}

if (state.toShowPrefs) {
FlakerPrefsDialog(
onDismissRequest = viewModel::closePrefs,
onConfirmAction = viewModel::updatePrefs,
currentValues = viewModel.getCurrentPrefs(),
)
}
}
}
}

@Composable
private fun NetworkRequestList(modifier: Modifier = Modifier, state: FlakerViewModel.ViewState) {
if (state.showNoRequests) {
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxSize()) {
Text(
text = stringResource(R.string.no_requests_yet),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp)
.align(Alignment.CenterHorizontally)
)
LazyColumn(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier.fillMaxSize()
) {
item {
Text(
text = stringResource(R.string.no_requests_yet),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp)

)
}
}
} else {
LazyColumn(modifier = modifier) {
Expand All @@ -90,6 +104,7 @@ class FlakerActivity : ComponentActivity() {
modifier = Modifier.padding(horizontal = 16.dp)
)
}

is NetworkRequestUi.DateItem -> {
Spacer(modifier = Modifier.size(8.dp))
Text(text = item.formattedString, modifier = Modifier.padding(horizontal = 16.dp))
Expand All @@ -106,6 +121,7 @@ class FlakerActivity : ComponentActivity() {
modifier: Modifier = Modifier,
state: FlakerViewModel.ViewState,
onToggleChange: (Boolean) -> Unit,
onPrefsClick: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
) {
LargeTopAppBar(
Expand All @@ -132,7 +148,7 @@ class FlakerActivity : ComponentActivity() {

Spacer(modifier = Modifier.size(4.dp))

IconButton(onClick = { /*TODO Add Prefs Dialog UI*/ }) {
IconButton(onClick = onPrefsClick) {
Icon(imageVector = Icons.Outlined.Settings, contentDescription = "Search network requests")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.rotlabs.flakerandroidretrofit

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import kotlin.math.round

private val PrefUiDtoSaver = Saver<FlakerPrefsUiDto, String>(
save = { "${it.delay}, ${it.failPercent}, ${it.variancePercent}" },
restore = { valuesString ->
valuesString.split(",").let {
FlakerPrefsUiDto(it[0].trim().toLong(), it[1].trim().toInt(), it[2].trim().toInt())
}
}
)

private const val HUNDRED_PERCENT = 100

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FlakerPrefsDialog(
modifier: Modifier = Modifier,
onDismissRequest: () -> Unit,
onConfirmAction: (FlakerPrefsUiDto) -> Unit,
currentValues: FlakerPrefsUiDto
) {
BackHandler {
onDismissRequest()
}

var newValues by rememberSaveable(stateSaver = PrefUiDtoSaver) { mutableStateOf(value = currentValues) }

val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()

Scaffold(
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
title = { Text(text = "Preferences", modifier = Modifier.padding(horizontal = 8.dp)) },
navigationIcon = {
IconButton(onClick = onDismissRequest) {
Icon(imageVector = Icons.Outlined.Close, contentDescription = "Dismiss")
}
},
actions = {
TextButton(
onClick = { onConfirmAction(newValues) }
) {
Text(
text = "Save",
modifier = Modifier.padding(horizontal = 16.dp)
)
}
},
scrollBehavior = scrollBehavior
)
}
) {
LazyColumn(modifier = Modifier.padding(it)) {
item {
val delayLabel = stringResource(id = R.string.delay_label)
val delayText = remember(newValues.delay) { "$delayLabel: ${newValues.delay}ms" }
val delayCurrentValue = remember(newValues.delay) { newValues.delay.toFloat() / HUNDRED_PERCENT }
PrefsItem(
title = delayText,
currentValue = delayCurrentValue,
onValueChange = { value ->
if (value >= 1f) {
newValues = newValues.copy(delay = (round(value).toLong() * HUNDRED_PERCENT))
}
},
discreteSteps = 9,
modifier = Modifier.padding(24.dp)
)

Spacer(modifier = Modifier.size(16.dp))
}

item {
val failPercentLabel = stringResource(id = R.string.fail_percentage_label)
val failPercentText = remember(newValues.failPercent) { "$failPercentLabel: ${newValues.failPercent}%" }
val failPercentCurrentValue = remember(newValues.failPercent) { newValues.failPercent.toFloat() }
PrefsItem(
title = failPercentText,
currentValue = failPercentCurrentValue,
onValueChange = { value -> newValues = newValues.copy(failPercent = round(value).toInt()) },
discreteSteps = 19,
modifier = Modifier.padding(24.dp)
)

Spacer(modifier = Modifier.size(16.dp))
}

item {
val variancePercentLabel = stringResource(id = R.string.variance_percentage_label)
val variancePercentText =
remember(newValues.variancePercent) { "$variancePercentLabel: ${newValues.variancePercent}%" }
val variancePercentCurrentValue =
remember(newValues.variancePercent) { newValues.variancePercent.toFloat() }
PrefsItem(
title = variancePercentText,
currentValue = variancePercentCurrentValue,
onValueChange = { value -> newValues = newValues.copy(variancePercent = round(value).toInt()) },
discreteSteps = 19,
modifier = Modifier.padding(24.dp)
)
}
}
}
}

@Composable
private fun PrefsItem(
modifier: Modifier = Modifier,
title: String,
minValue: Float = 0f,
maxValue: Float = 100f,
currentValue: Float,
onValueChange: (Float) -> Unit,
discreteSteps: Int = 0,
) {
Column(modifier = modifier) {
Text(text = title)

Spacer(modifier = Modifier.size(8.dp))

Slider(
value = currentValue,
onValueChange = onValueChange,
valueRange = minValue..maxValue,
steps = discreteSteps
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.rotlabs.flakerandroidretrofit

data class FlakerPrefsUiDto(
val delay: Long,
val failPercent: Int,
val variancePercent: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class FlakerViewModel(

data class ViewState(
val isFlakerOn: Boolean = false,
val networkRequests: List<NetworkRequestUi> = emptyList()
val networkRequests: List<NetworkRequestUi> = emptyList(),
val toShowPrefs: Boolean = false,
) {
val showNoRequests: Boolean
get() = networkRequests.isEmpty()
Expand Down Expand Up @@ -68,6 +69,29 @@ class FlakerViewModel(
flakerRepo.saveShouldIntercept(value)
}

fun openPrefs() {
viewModelScope.launch { _viewStateFlow.emit(_viewStateFlow.value.copy(toShowPrefs = true)) }
}

fun getCurrentPrefs(): FlakerPrefsUiDto {
return FlakerPrefsUiDto(
delay = flakerRepo.getDelayValue(),
failPercent = flakerRepo.getFailPercent(),
variancePercent = flakerRepo.getVariancePercent()
)
}

fun closePrefs() {
viewModelScope.launch { _viewStateFlow.emit(_viewStateFlow.value.copy(toShowPrefs = false)) }
}

fun updatePrefs(flakerPrefsUiDto: FlakerPrefsUiDto) {
flakerRepo.saveDelayValue(flakerPrefsUiDto.delay)
flakerRepo.saveFailPercent(flakerPrefsUiDto.failPercent)
flakerRepo.saveVariancePercent(flakerPrefsUiDto.variancePercent)
closePrefs()
}

companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
Expand Down
3 changes: 3 additions & 0 deletions flaker-android-retrofit/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="no_requests_yet">No requests yet</string>
<string name="delay_label">Delay</string>
<string name="fail_percentage_label">Fail Percentage</string>
<string name="variance_percentage_label">Variance Percentage</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,10 @@ class FlakerRepo(private val context: Context) {
fun saveVariancePercent(variancePercent: Int) = flakerPrefs.saveVariancePercent(variancePercent)

fun saveShouldIntercept(shouldIntercept: Boolean) = flakerPrefs.saveShouldIntercept(shouldIntercept)

fun getDelayValue() = flakerPrefs.getDelay()

fun getFailPercent() = flakerPrefs.getFailPercent()

fun getVariancePercent() = flakerPrefs.getVariancePercent()
}

0 comments on commit 4e04049

Please sign in to comment.