@@ -2,7 +2,6 @@ package io.github.sds100.keymapper.base.constraints
22
33import androidx.compose.foundation.layout.Arrangement
44import androidx.compose.foundation.layout.Box
5- import androidx.compose.foundation.layout.Column
65import androidx.compose.foundation.layout.PaddingValues
76import androidx.compose.foundation.layout.calculateEndPadding
87import androidx.compose.foundation.layout.calculateStartPadding
@@ -15,25 +14,19 @@ import androidx.compose.foundation.lazy.grid.GridCells
1514import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1615import androidx.compose.foundation.lazy.grid.items
1716import androidx.compose.material.icons.Icons
18- import androidx.compose.material.icons.automirrored.rounded.ArrowBack
1917import 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
2120import androidx.compose.material3.BottomAppBar
2221import androidx.compose.material3.CircularProgressIndicator
23- import androidx.compose.material3.DockedSearchBar
2422import androidx.compose.material3.ExperimentalMaterial3Api
25- import androidx.compose.material3.Icon
26- import androidx.compose.material3.IconButton
2723import androidx.compose.material3.MaterialTheme
2824import androidx.compose.material3.Scaffold
29- import androidx.compose.material3.SearchBarDefaults
3025import androidx.compose.material3.Surface
3126import androidx.compose.material3.Text
27+ import androidx.compose.material3.TopAppBar
3228import androidx.compose.runtime.Composable
3329import androidx.compose.runtime.getValue
34- import androidx.compose.runtime.mutableStateOf
35- import androidx.compose.runtime.saveable.rememberSaveable
36- import androidx.compose.runtime.setValue
3730import androidx.compose.ui.Alignment
3831import androidx.compose.ui.Modifier
3932import androidx.compose.ui.platform.LocalLayoutDirection
@@ -48,25 +41,28 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
4841import io.github.sds100.keymapper.base.R
4942import io.github.sds100.keymapper.base.compose.KeyMapperTheme
5043import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
44+ import io.github.sds100.keymapper.base.utils.ui.compose.SearchAppBarActions
5145import 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
5248import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
5349import io.github.sds100.keymapper.common.utils.State
5450import kotlinx.coroutines.flow.update
5551
5652@Composable
5753fun 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
7672private 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
234169private 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() {
285241private 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