Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.material.icons.outlined.KeyboardArrowUp
Expand All @@ -24,6 +29,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -55,15 +61,16 @@ internal fun KonfeatureScreen(
onResetAllClick = viewModel::onResetAllClick,
onCollapseAllClick = viewModel::onCollapseAllClick,
onHeaderClick = viewModel::onConfigHeaderClick,
onEditClick = viewModel::onEditClick
onEditClick = viewModel::onEditClick,
onSearchQueryChange = viewModel::onSearchQueryChanged,
)

state.editDialogState?.let { dialogState ->
EditConfigValueDialog(
state = dialogState,
onValueChange = viewModel::onValueChanged,
onValueReset = viewModel::onValueReset,
onDismissRequest = viewModel::onEditDialogCloseClik
onDismissRequest = viewModel::onEditDialogCloseClicked
)
}
}
Expand All @@ -77,47 +84,121 @@ internal fun KonfeatureLayout(
onCollapseAllClick: () -> Unit,
onResetAllClick: () -> Unit,
onHeaderClick: (String) -> Unit,
onSearchQueryChange: (String) -> Unit,
) {
LazyColumn {
stickyHeader {
Row(
modifier = Modifier
.background(colorResource(id = CoreR.color.super_light_gray))
.padding(horizontal = 16.dp, vertical = 4.dp)
) {
Button(onClick = onRefreshClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_refresh))
}
Spacer(modifier = Modifier.weight(1f))
Button(onClick = onCollapseAllClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_collapse_all))
}
Spacer(modifier = Modifier.weight(1f))
Button(onClick = onResetAllClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_reset_all))
}
KonfeatureHeader(
searchQuery = state.searchQuery,
onSearchQueryChange = onSearchQueryChange,
onRefreshClick = onRefreshClick,
onCollapseAllClick = onCollapseAllClick,
onResetAllClick = onResetAllClick,
)
}

if (state.shouldShowEmptySearchItemsHint) {
item {
Text(
text = stringResource(R.string.konfeature_plugin_search_empty),
modifier = Modifier.padding(16.dp)
)
}
}

state.items.forEach { item ->
state.filteredItems.forEach { item ->
if (item is KonfeatureItem.Config) {
item(item.name) {
ConfigItem(
item = item,
isCollapsed = item.name in state.collapsedConfigs,
isCollapsed = !state.isSearchActive && item.name in state.collapsedConfigs,
onHeaderClick = onHeaderClick
)
}
}

if (item is KonfeatureItem.Value && item.configName !in state.collapsedConfigs) {
val isExpanded = state.isSearchActive || item is KonfeatureItem.Value &&
item.configName !in state.collapsedConfigs
if (item is KonfeatureItem.Value && isExpanded) {
item(item.key) { ValueItem(item = item, onEditClick) }
item { Divider(modifier = Modifier.fillMaxWidth()) }
}
}
}
}

@Composable
private fun KonfeatureHeader(
searchQuery: String,
onSearchQueryChange: (String) -> Unit,
onRefreshClick: () -> Unit,
onCollapseAllClick: () -> Unit,
onResetAllClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(colorResource(id = CoreR.color.super_light_gray))
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(8.dp))
KonfeatureSearchBar(
query = searchQuery,
onQueryChange = onSearchQueryChange,
modifier = Modifier
Comment thread
TopHlop marked this conversation as resolved.
Outdated
)
Spacer(modifier = Modifier.height(8.dp))
Row {
Button(onClick = onRefreshClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_refresh))
}
Spacer(modifier = Modifier.weight(1f))
Button(onClick = onCollapseAllClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_collapse_all))
}
Spacer(modifier = Modifier.weight(1f))
Button(onClick = onResetAllClick) {
Text(text = stringResource(id = R.string.konfeature_plugin_reset_all))
}
}
}
}

@Composable
private fun KonfeatureSearchBar(
query: String,
onQueryChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier.fillMaxWidth(),
placeholder = {
Text(text = stringResource(R.string.konfeature_plugin_search_hint))
},
leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null
)
},
trailingIcon = {
if (query.isNotEmpty()) {
IconButton(onClick = { onQueryChange("") }) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = stringResource(R.string.konfeature_plugin_search_clear)
)
}
}
},
singleLine = true,
colors = TextFieldDefaults.outlinedTextFieldColors(
backgroundColor = Color.White
)
)
}

@Composable
private fun ConfigItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ internal class KonfeatureViewModel(
private val konfeature: Konfeature,
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
) : PluginViewModel() {

private val _state = MutableStateFlow(KonfeatureViewState())

val state: Flow<KonfeatureViewState> = _state.asStateFlow()
Expand Down Expand Up @@ -85,13 +84,23 @@ internal class KonfeatureViewModel(
_state.update { it.copy(editDialogState = EditDialogState(key, value, isDebugSource)) }
}

fun onEditDialogCloseClik() {
fun onEditDialogCloseClicked() {
_state.update { it.copy(editDialogState = null) }
}

fun onSearchQueryChanged(query: String) {
_state.update { state ->
val filteredItems = filterItems(state.items, query)
state.copy(searchQuery = query, filteredItems = filteredItems)
}
}

private suspend fun updateItems() {
val items = withContext(Dispatchers.IO) { getItems(konfeature) }
_state.update { it.copy(items = items) }
_state.update { state ->
val filteredItems = filterItems(items, state.searchQuery)
state.copy(items = items, filteredItems = filteredItems)
}
}

private fun getItems(konfeature: Konfeature): List<KonfeatureItem> {
Expand Down Expand Up @@ -152,6 +161,23 @@ internal class KonfeatureViewModel(
else -> "Unknown"
}
}

private fun filterItems(items: List<KonfeatureItem>, query: String): List<KonfeatureItem> {
if (query.isBlank()) return items

val formattedQuery = query.lowercase()
val matchingItems = items
.filterIsInstance<KonfeatureItem.Value>()
.filter { it.key.lowercase().contains(formattedQuery) }
val matchingConfigNames = matchingItems.map { it.configName }.toSet()
Comment thread
TopHlop marked this conversation as resolved.
Outdated

return items.filter { item ->
when (item) {
is KonfeatureItem.Config -> item.name in matchingConfigNames
is KonfeatureItem.Value -> item.key.lowercase().contains(formattedQuery)
}
}
}
}


Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.redmadrobot.debug.plugin.konfeature.ui.data

internal data class KonfeatureViewState(
val searchQuery: String = "",
val collapsedConfigs: Set<String> = emptySet(),
val items: List<KonfeatureItem> = emptyList(),
val filteredItems: List<KonfeatureItem> = emptyList(),
val editDialogState: EditDialogState? = null
)
) {
val isSearchActive: Boolean
get() = searchQuery.isNotBlank()
val shouldShowEmptySearchItemsHint
get() = isSearchActive && filteredItems.none { it is KonfeatureItem.Value }
}
4 changes: 4 additions & 0 deletions plugins/plugin-konfeature/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@
<string name="konfeature_plugin_refresh">Refresh</string>
<string name="konfeature_plugin_collapse_all">Collapse All</string>
<string name="konfeature_plugin_reset_all">Reset All</string>

<string name="konfeature_plugin_search_hint">Search by key…</string>
<string name="konfeature_plugin_search_clear">Clear search</string>
<string name="konfeature_plugin_search_empty">No toggles found</string>
</resources>
Loading