Skip to content

Commit 6425d2c

Browse files
authored
Merge pull request #1910 from keymapperorg/copilot/categorise-constraints-similar-actions
Categorise constraints similar to actions
2 parents ab0bb57 + 5ac9a25 commit 6425d2c

File tree

6 files changed

+293
-200
lines changed

6 files changed

+293
-200
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- #1414 constraint for when the keyboard is showing.
1010
- #1900 log to logcat if extra logging is enabled.
1111
- #1902 add toggle next to record trigger button to use PRO mode.
12+
- #1909 categorise constraints similar to actions.
1213

1314
## Bug fixes
1415

base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt

Lines changed: 108 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package io.github.sds100.keymapper.base.constraints
22

33
import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.foundation.layout.Box
5-
import androidx.compose.foundation.layout.Column
65
import androidx.compose.foundation.layout.PaddingValues
76
import androidx.compose.foundation.layout.calculateEndPadding
87
import androidx.compose.foundation.layout.calculateStartPadding
@@ -15,25 +14,19 @@ import androidx.compose.foundation.lazy.grid.GridCells
1514
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1615
import androidx.compose.foundation.lazy.grid.items
1716
import androidx.compose.material.icons.Icons
18-
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
1917
import androidx.compose.material.icons.rounded.Android
20-
import androidx.compose.material.icons.rounded.Search
18+
import androidx.compose.material.icons.rounded.Bluetooth
19+
import androidx.compose.material.icons.rounded.Wifi
2120
import androidx.compose.material3.BottomAppBar
2221
import androidx.compose.material3.CircularProgressIndicator
23-
import androidx.compose.material3.DockedSearchBar
2422
import androidx.compose.material3.ExperimentalMaterial3Api
25-
import androidx.compose.material3.Icon
26-
import androidx.compose.material3.IconButton
2723
import androidx.compose.material3.MaterialTheme
2824
import androidx.compose.material3.Scaffold
29-
import androidx.compose.material3.SearchBarDefaults
3025
import androidx.compose.material3.Surface
3126
import androidx.compose.material3.Text
27+
import androidx.compose.material3.TopAppBar
3228
import androidx.compose.runtime.Composable
3329
import androidx.compose.runtime.getValue
34-
import androidx.compose.runtime.mutableStateOf
35-
import androidx.compose.runtime.saveable.rememberSaveable
36-
import androidx.compose.runtime.setValue
3730
import androidx.compose.ui.Alignment
3831
import androidx.compose.ui.Modifier
3932
import androidx.compose.ui.platform.LocalLayoutDirection
@@ -48,25 +41,28 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
4841
import io.github.sds100.keymapper.base.R
4942
import io.github.sds100.keymapper.base.compose.KeyMapperTheme
5043
import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
44+
import io.github.sds100.keymapper.base.utils.ui.compose.SearchAppBarActions
5145
import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemFixedHeight
46+
import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup
47+
import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemHeader
5248
import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
5349
import io.github.sds100.keymapper.common.utils.State
5450
import kotlinx.coroutines.flow.update
5551

5652
@Composable
5753
fun ChooseConstraintScreen(modifier: Modifier = Modifier, viewModel: ChooseConstraintViewModel) {
58-
val listItems by viewModel.listItems.collectAsStateWithLifecycle()
54+
val state by viewModel.groups.collectAsStateWithLifecycle()
5955
val query by viewModel.searchQuery.collectAsStateWithLifecycle()
6056

6157
TimeConstraintBottomSheet(viewModel)
6258

6359
ChooseConstraintScreen(
6460
modifier = modifier,
65-
state = listItems,
61+
state = state,
6662
query = query,
6763
onQueryChange = { newQuery -> viewModel.searchQuery.update { newQuery } },
6864
onCloseSearch = { viewModel.searchQuery.update { null } },
69-
onClickAction = viewModel::onListItemClick,
65+
onClickConstraint = viewModel::onListItemClick,
7066
onNavigateBack = viewModel::onNavigateBack,
7167
)
7268
}
@@ -75,78 +71,30 @@ fun ChooseConstraintScreen(modifier: Modifier = Modifier, viewModel: ChooseConst
7571
@Composable
7672
private fun ChooseConstraintScreen(
7773
modifier: Modifier = Modifier,
78-
state: State<List<SimpleListItemModel>>,
74+
state: State<List<SimpleListItemGroup>>,
7975
query: String? = null,
8076
onQueryChange: (String) -> Unit = {},
8177
onCloseSearch: () -> Unit = {},
82-
onClickAction: (String) -> Unit = {},
78+
onClickConstraint: (String) -> Unit = {},
8379
onNavigateBack: () -> Unit = {},
8480
) {
85-
var isExpanded: Boolean by rememberSaveable { mutableStateOf(false) }
86-
8781
Scaffold(
8882
modifier = modifier.displayCutoutPadding(),
83+
topBar = {
84+
TopAppBar(
85+
title = { Text(stringResource(R.string.choose_constraint_title)) },
86+
)
87+
},
8988
bottomBar = {
9089
BottomAppBar(
9190
modifier = Modifier.imePadding(),
9291
actions = {
93-
IconButton(onClick = {
94-
if (isExpanded) {
95-
onCloseSearch()
96-
isExpanded = false
97-
} else {
98-
onNavigateBack()
99-
}
100-
}) {
101-
Icon(
102-
Icons.AutoMirrored.Rounded.ArrowBack,
103-
contentDescription = stringResource(
104-
R.string.bottom_app_bar_back_content_description,
105-
),
106-
)
107-
}
108-
109-
DockedSearchBar(
110-
modifier = Modifier.align(Alignment.CenterVertically),
111-
inputField = {
112-
SearchBarDefaults.InputField(
113-
modifier = Modifier.align(Alignment.CenterVertically),
114-
onSearch = {
115-
onQueryChange(it)
116-
isExpanded = false
117-
},
118-
leadingIcon = {
119-
Icon(
120-
Icons.Rounded.Search,
121-
contentDescription = null,
122-
)
123-
},
124-
enabled = state is State.Data,
125-
placeholder = { Text(stringResource(R.string.search_placeholder)) },
126-
query = query ?: "",
127-
onQueryChange = onQueryChange,
128-
expanded = isExpanded,
129-
onExpandedChange = { expanded ->
130-
if (expanded) {
131-
isExpanded = true
132-
} else {
133-
onCloseSearch()
134-
isExpanded = false
135-
}
136-
},
137-
)
138-
},
139-
// This is false to prevent an empty "content" showing underneath.
140-
expanded = isExpanded,
141-
onExpandedChange = { expanded ->
142-
if (expanded) {
143-
isExpanded = true
144-
} else {
145-
onCloseSearch()
146-
isExpanded = false
147-
}
148-
},
149-
content = {},
92+
SearchAppBarActions(
93+
onCloseSearch = onCloseSearch,
94+
onNavigateBack = onNavigateBack,
95+
onQueryChange = onQueryChange,
96+
enabled = state is State.Data,
97+
query = query,
15098
)
15199
},
152100
)
@@ -167,33 +115,20 @@ private fun ChooseConstraintScreen(
167115
),
168116

169117
) {
170-
Column {
171-
Text(
172-
modifier = Modifier.padding(
173-
start = 16.dp,
174-
end = 16.dp,
175-
top = 16.dp,
176-
bottom = 8.dp,
177-
),
178-
text = stringResource(R.string.choose_constraint_title),
179-
style = MaterialTheme.typography.titleLarge,
180-
)
181-
182-
when (state) {
183-
State.Loading -> LoadingScreen(modifier = Modifier.fillMaxSize())
118+
when (state) {
119+
State.Loading -> LoadingScreen(modifier = Modifier.fillMaxSize())
184120

185-
is State.Data -> {
186-
if (state.data.isEmpty()) {
187-
EmptyScreen(
188-
modifier = Modifier.fillMaxSize(),
189-
)
190-
} else {
191-
ListScreen(
192-
modifier = Modifier.fillMaxSize(),
193-
listItems = state.data,
194-
onClickAction = onClickAction,
195-
)
196-
}
121+
is State.Data -> {
122+
if (state.data.isEmpty()) {
123+
EmptyScreen(
124+
modifier = Modifier.fillMaxSize(),
125+
)
126+
} else {
127+
ListScreen(
128+
modifier = Modifier.fillMaxSize(),
129+
groups = state.data,
130+
onClickConstraint = onClickConstraint,
131+
)
197132
}
198133
}
199134
}
@@ -233,8 +168,8 @@ private fun EmptyScreen(modifier: Modifier = Modifier) {
233168
@Composable
234169
private fun ListScreen(
235170
modifier: Modifier = Modifier,
236-
listItems: List<SimpleListItemModel>,
237-
onClickAction: (String) -> Unit,
171+
groups: List<SimpleListItemGroup>,
172+
onClickConstraint: (String) -> Unit,
238173
) {
239174
LazyVerticalGrid(
240175
modifier = modifier,
@@ -243,12 +178,21 @@ private fun ListScreen(
243178
verticalArrangement = Arrangement.spacedBy(8.dp),
244179
horizontalArrangement = Arrangement.spacedBy(8.dp),
245180
) {
246-
items(listItems, key = { it.id }) { model ->
247-
SimpleListItemFixedHeight(
248-
modifier = Modifier.fillMaxWidth(),
249-
model = model,
250-
onClick = { onClickAction(model.id) },
251-
)
181+
for (group in groups) {
182+
stickyHeader(contentType = "header") {
183+
SimpleListItemHeader(modifier = Modifier.fillMaxWidth(), text = group.header)
184+
}
185+
186+
items(
187+
group.items,
188+
contentType = { "list_item" },
189+
) { model ->
190+
SimpleListItemFixedHeight(
191+
modifier = Modifier.fillMaxWidth(),
192+
model = model,
193+
onClick = { onClickConstraint(model.id) },
194+
)
195+
}
252196
}
253197
}
254198
}
@@ -261,18 +205,30 @@ private fun PreviewList() {
261205
query = "Search query",
262206
state = State.Data(
263207
listOf(
264-
SimpleListItemModel(
265-
"app",
266-
title = "App in foreground",
267-
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
208+
SimpleListItemGroup(
209+
header = "Apps",
210+
items = listOf(
211+
SimpleListItemModel(
212+
"app1",
213+
title = "App in foreground",
214+
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
215+
),
216+
SimpleListItemModel(
217+
"app2",
218+
title = "App not in foreground",
219+
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
220+
),
221+
),
268222
),
269-
SimpleListItemModel(
270-
"app",
271-
title = "App not in foreground",
272-
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
273-
subtitle = "Error",
274-
isSubtitleError = true,
275-
isEnabled = false,
223+
SimpleListItemGroup(
224+
header = "Bluetooth",
225+
items = listOf(
226+
SimpleListItemModel(
227+
"bt1",
228+
title = "Bluetooth device connected",
229+
icon = ComposeIconInfo.Vector(Icons.Rounded.Bluetooth),
230+
),
231+
),
276232
),
277233
),
278234
),
@@ -285,21 +241,42 @@ private fun PreviewList() {
285241
private fun PreviewGrid() {
286242
KeyMapperTheme {
287243
ChooseConstraintScreen(
288-
query = "Search query",
289244
state = State.Data(
290245
listOf(
291-
SimpleListItemModel(
292-
"app1",
293-
title = "App in foreground",
294-
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
246+
SimpleListItemGroup(
247+
header = "Apps",
248+
items = listOf(
249+
SimpleListItemModel(
250+
"app1",
251+
title = "App in foreground",
252+
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
253+
),
254+
SimpleListItemModel(
255+
"app2",
256+
title = "App not in foreground",
257+
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
258+
),
259+
),
295260
),
296-
SimpleListItemModel(
297-
"app2",
298-
title = "App not in foreground",
299-
icon = ComposeIconInfo.Vector(Icons.Rounded.Android),
300-
subtitle = "Error",
301-
isSubtitleError = true,
302-
isEnabled = false,
261+
SimpleListItemGroup(
262+
header = "WiFi",
263+
items = listOf(
264+
SimpleListItemModel(
265+
"wifi1",
266+
title = "WiFi is on",
267+
icon = ComposeIconInfo.Vector(Icons.Rounded.Wifi),
268+
subtitle = "Requires root",
269+
isSubtitleError = true,
270+
),
271+
SimpleListItemModel(
272+
"wifi2",
273+
title = "WiFi is off",
274+
icon = ComposeIconInfo.Vector(Icons.Rounded.Wifi),
275+
subtitle = "Requires root",
276+
isSubtitleError = true,
277+
isEnabled = false,
278+
),
279+
),
303280
),
304281
),
305282
),

0 commit comments

Comments
 (0)