@@ -8,9 +8,15 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
88import androidx.compose.foundation.lazy.grid.rememberLazyGridState
99import androidx.compose.foundation.lazy.rememberLazyListState
1010import androidx.compose.foundation.rememberScrollbarAdapter
11+ import androidx.compose.foundation.shape.RoundedCornerShape
12+ import androidx.compose.foundation.text.input.TextFieldState
13+ import androidx.compose.foundation.text.input.rememberTextFieldState
14+ import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
1115import androidx.compose.foundation.text.selection.SelectionContainer
1216import androidx.compose.material.icons.Icons
17+ import androidx.compose.material.icons.filled.Clear
1318import androidx.compose.material.icons.filled.QuestionMark
19+ import androidx.compose.material.icons.filled.Search
1420import androidx.compose.material.icons.outlined.*
1521import androidx.compose.material3.*
1622import androidx.compose.runtime.*
@@ -19,28 +25,91 @@ import androidx.compose.ui.Modifier
1925import androidx.compose.ui.graphics.Color
2026import androidx.compose.ui.text.style.TextOverflow
2127import androidx.compose.ui.unit.dp
28+ import dalvikus.composeapp.generated.resources.Res
29+ import dalvikus.composeapp.generated.resources.all_elements
30+ import dalvikus.composeapp.generated.resources.resources_search_placeholder
31+ import io.github.composegears.valkyrie.MatchCase
32+ import io.github.composegears.valkyrie.RegularExpression
2233import me.lkl.dalvikus.tree.archive.ApkNode
2334import me.lkl.dalvikus.ui.uiTreeRoot
2435import me.lkl.dalvikus.util.CollapseCard
36+ import me.lkl.dalvikus.util.SearchOptions
37+ import me.lkl.dalvikus.util.createSearchMatcher
38+ import me.lkl.dalvikus.util.to0xHex
39+ import org.jetbrains.compose.resources.stringResource
2540
41+ @OptIn(ExperimentalMaterial3Api ::class )
2642@Composable
2743fun ResourcesView () {
2844 val scope = rememberCoroutineScope()
45+ val searchBarState = rememberSearchBarState()
46+ val searchFieldState = rememberTextFieldState()
47+ var searchOptions by remember { mutableStateOf(SearchOptions ()) }
48+
49+ val searchField =
50+ @Composable {
51+ SearchBarDefaults .InputField (
52+ searchBarState = searchBarState,
53+ textFieldState = searchFieldState,
54+ onSearch = {},
55+ placeholder = {
56+ Text (
57+ stringResource(Res .string.resources_search_placeholder),
58+ maxLines = 1 ,
59+ overflow = TextOverflow .Ellipsis
60+ )
61+ },
62+ leadingIcon = {
63+ Icon (Icons .Default .Search , contentDescription = null )
64+ },
65+ trailingIcon = {
66+ Row {
67+ if (searchFieldState.text.isNotEmpty()) {
68+ IconToggleButton (
69+ checked = searchOptions.useRegex,
70+ onCheckedChange = { searchOptions = searchOptions.copy(useRegex = it) }) {
71+ Icon (Icons .Filled .RegularExpression , contentDescription = " Regular expression" )
72+ }
73+ IconToggleButton (
74+ checked = searchOptions.caseSensitive,
75+ onCheckedChange = { searchOptions = searchOptions.copy(caseSensitive = it) }) {
76+ Icon (Icons .Filled .MatchCase , contentDescription = " Case sensitive" )
77+ }
78+ IconButton (onClick = {
79+ searchFieldState.setTextAndPlaceCursorAtEnd(" " )
80+ }) {
81+ Icon (Icons .Default .Clear , contentDescription = " Clear" )
82+ }
83+ }
84+ }
85+ }
86+ )
87+ }
2988
3089 Scaffold (
31- containerColor = Color .Transparent
90+ containerColor = Color .Transparent ,
91+ topBar = {
92+ TopSearchBar (
93+ state = searchBarState,
94+ inputField = searchField,
95+ shape = RoundedCornerShape (16 .dp),
96+ tonalElevation = 4 .dp,
97+ shadowElevation = 0 .dp,
98+ windowInsets = SearchBarDefaults .windowInsets,
99+ modifier = Modifier .fillMaxWidth().padding(top = 8 .dp),
100+ scrollBehavior = null
101+ )
102+ }
32103 ) { innerPadding ->
33104 Column (
34105 modifier = Modifier .fillMaxSize().padding(innerPadding)
35106 ) {
36- ApkResourceCards ()
107+ ApkResourceCards (searchFieldState, searchOptions )
37108 }
38109 }
39110}
40111
41112val resourceTypesIcons = mapOf (
42- " all" to Icons .Outlined .Android ,
43-
44113 " anim" to Icons .Outlined .PlayArrow ,
45114 " animator" to Icons .Outlined .Movie ,
46115 " attr" to Icons .Outlined .Tune ,
@@ -63,22 +132,31 @@ val resourceTypesIcons = mapOf(
63132)
64133
65134@Composable
66- private fun ApkResourceCards () {
67- // TODO add search bar for resource ids in base 16, base 10, and resource name.
135+ private fun ApkResourceCards (searchFieldState : TextFieldState , searchOptions : SearchOptions ) {
68136 val treeRootChildren by uiTreeRoot.childrenFlow.collectAsState()
69137 val apks = treeRootChildren.filterIsInstance<ApkNode >()
70138
71139 val gridState = rememberLazyGridState()
72140 var selectedResType by remember { mutableStateOf(" all" ) }
73141
142+ val searchMatcher by remember(searchFieldState.text, searchOptions) {
143+ derivedStateOf { createSearchMatcher(searchFieldState.text.toString().replace(" " , " " ), searchOptions) }
144+ }
145+
74146 Column (modifier = Modifier .fillMaxSize()) {
75- // Filter chips row
76147 FlowRow (
77148 modifier = Modifier
78149 .fillMaxWidth()
79150 .padding(8 .dp),
80151 horizontalArrangement = Arrangement .spacedBy(8 .dp)
81152 ) {
153+ FilterChip (
154+ // TODO remove elevation = null when https://youtrack.jetbrains.com/issue/CMP-2868 is fixed.
155+ elevation = null ,
156+ selected = selectedResType == " all" ,
157+ onClick = { selectedResType = " all" },
158+ label = { Text (stringResource(Res .string.all_elements)) }
159+ )
82160 resourceTypesIcons.forEach { (type, icon) ->
83161 FilterChip (
84162 // TODO remove elevation = null when https://youtrack.jetbrains.com/issue/CMP-2868 is fixed.
@@ -118,6 +196,8 @@ private fun ApkResourceCards() {
118196 if (resSpecList == null ) return @CollapseCard
119197 val resourceSpecs = resSpecList.filter { resourceSpec ->
120198 resourceSpec != null && (selectedResType == " all" || resourceSpec.type.name == selectedResType)
199+ && (searchMatcher == null || searchMatcher!! (resourceSpec.name)
200+ || searchMatcher!! (resourceSpec.id.toLong().toString()) || searchMatcher!! (resourceSpec.id.toLong().to0xHex()))
121201 }
122202
123203 val innerListState = rememberLazyListState()
@@ -134,9 +214,9 @@ private fun ApkResourceCards() {
134214 items(resourceSpecs.size) { index ->
135215 val resourceSpec = resourceSpecs[index]
136216 val resId = resourceSpec!! .id
137- val resIdPkg = String .format(" %02X " , resId.packageId)
138- val resIdTypeId = String .format(" %02X " , resId.type)
139- val resIdEntryNumber = String .format(" %04X " , resId.entry)
217+ val resIdPkg = String .format(" %02x " , resId.packageId)
218+ val resIdTypeId = String .format(" %02x " , resId.type)
219+ val resIdEntryNumber = String .format(" %04x " , resId.entry)
140220
141221 ListItem (
142222 headlineContent = {
0 commit comments