Skip to content

Commit c29516a

Browse files
committed
feat: add search to apps tab
1 parent 67a5616 commit c29516a

File tree

2 files changed

+194
-33
lines changed

2 files changed

+194
-33
lines changed

app/src/main/java/com/raival/compose/file/explorer/screen/main/tab/apps/AppsTab.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ class AppsTab : Tab() {
3030

3131
var selectedChoice by mutableIntStateOf(0)
3232
var previewAppDialog by mutableStateOf<AppHolder?>(null)
33-
33+
var isSearchPanelOpen by mutableStateOf(false)
34+
var searchQuery by mutableStateOf(emptyString)
35+
var isSearching by mutableStateOf(false)
3436
var isLoading by mutableStateOf(false)
3537

3638
override fun onTabResumed() {

app/src/main/java/com/raival/compose/file/explorer/screen/main/tab/apps/compose/AppsTabContentView.kt

Lines changed: 191 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,73 @@
11
package com.raival.compose.file.explorer.screen.main.tab.apps.compose
22

3+
import androidx.activity.compose.BackHandler
4+
import androidx.compose.animation.AnimatedVisibility
35
import androidx.compose.foundation.layout.Arrangement
46
import androidx.compose.foundation.layout.Box
57
import androidx.compose.foundation.layout.Column
68
import androidx.compose.foundation.layout.ColumnScope
79
import androidx.compose.foundation.layout.Row
810
import androidx.compose.foundation.layout.fillMaxSize
911
import androidx.compose.foundation.layout.fillMaxWidth
12+
import androidx.compose.foundation.layout.height
1013
import androidx.compose.foundation.layout.padding
1114
import androidx.compose.foundation.layout.size
1215
import androidx.compose.foundation.lazy.LazyColumn
1316
import androidx.compose.foundation.lazy.items
17+
import androidx.compose.foundation.shape.CircleShape
1418
import androidx.compose.foundation.shape.RoundedCornerShape
19+
import androidx.compose.foundation.text.KeyboardActions
20+
import androidx.compose.foundation.text.KeyboardOptions
21+
import androidx.compose.material.icons.Icons
22+
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
23+
import androidx.compose.material.icons.rounded.Cancel
24+
import androidx.compose.material.icons.rounded.Pause
25+
import androidx.compose.material.icons.rounded.Search
26+
import androidx.compose.material3.AlertDialogDefaults
1527
import androidx.compose.material3.CircularProgressIndicator
28+
import androidx.compose.material3.FloatingActionButton
29+
import androidx.compose.material3.Icon
30+
import androidx.compose.material3.IconButton
1631
import androidx.compose.material3.SegmentedButton
1732
import androidx.compose.material3.SegmentedButtonDefaults
1833
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
1934
import androidx.compose.material3.Text
2035
import androidx.compose.material3.TextButton
36+
import androidx.compose.material3.TextField
37+
import androidx.compose.material3.TextFieldDefaults
2138
import androidx.compose.runtime.Composable
2239
import androidx.compose.runtime.LaunchedEffect
2340
import androidx.compose.ui.Alignment
2441
import androidx.compose.ui.Modifier
2542
import androidx.compose.ui.draw.alpha
2643
import androidx.compose.ui.draw.clip
44+
import androidx.compose.ui.graphics.Color
2745
import androidx.compose.ui.graphics.FilterQuality
2846
import androidx.compose.ui.layout.ContentScale
2947
import androidx.compose.ui.res.painterResource
3048
import androidx.compose.ui.res.stringResource
3149
import androidx.compose.ui.text.font.FontWeight
50+
import androidx.compose.ui.text.input.ImeAction
3251
import androidx.compose.ui.unit.dp
3352
import androidx.compose.ui.unit.sp
3453
import coil3.compose.AsyncImage
3554
import com.raival.compose.file.explorer.App.Companion.globalClass
3655
import com.raival.compose.file.explorer.R
3756
import com.raival.compose.file.explorer.common.compose.BottomSheetDialog
3857
import com.raival.compose.file.explorer.common.compose.Space
58+
import com.raival.compose.file.explorer.common.compose.block
59+
import com.raival.compose.file.explorer.common.extension.emptyString
3960
import com.raival.compose.file.explorer.common.extension.toFormattedSize
4061
import com.raival.compose.file.explorer.screen.main.tab.apps.AppsTab
62+
import com.raival.compose.file.explorer.screen.main.tab.apps.modal.AppHolder
4163
import com.raival.compose.file.explorer.screen.main.tab.files.compose.ItemRow
4264
import com.raival.compose.file.explorer.screen.main.tab.files.compose.ItemRowIcon
4365
import com.raival.compose.file.explorer.screen.main.tab.files.modal.DocumentHolder
4466
import com.raival.compose.file.explorer.screen.main.tab.files.task.CopyTask
67+
import kotlinx.coroutines.CoroutineScope
68+
import kotlinx.coroutines.Dispatchers
69+
import kotlinx.coroutines.delay
70+
import kotlinx.coroutines.launch
4571
import java.io.File
4672

4773
@Composable
@@ -65,6 +91,27 @@ fun ColumnScope.AppsTabContentView(tab: AppsTab) {
6591
}
6692
}
6793

94+
BackHandler(tab.isSearchPanelOpen || tab.isSearching || tab.isLoading) {
95+
if (tab.isSearching) {
96+
tab.isSearching = false
97+
}
98+
99+
if (tab.isSearchPanelOpen) {
100+
tab.isSearchPanelOpen = false
101+
}
102+
103+
tab.appsList.clear()
104+
105+
when (tab.selectedChoice) {
106+
0 -> tab.appsList.addAll(tab.userApps)
107+
1 -> tab.appsList.addAll(tab.systemApps)
108+
2 -> {
109+
tab.appsList.addAll(tab.userApps)
110+
tab.appsList.addAll(tab.systemApps)
111+
}
112+
}
113+
}
114+
68115
if (tab.previewAppDialog != null) {
69116
val selectedApp = tab.previewAppDialog!!
70117
val details = arrayListOf<Pair<String, String>>().apply {
@@ -170,53 +217,165 @@ fun ColumnScope.AppsTabContentView(tab: AppsTab) {
170217
}
171218

172219
item {
173-
Space(70.dp)
220+
Space(150.dp)
174221
}
175222
}
176223

177-
SingleChoiceSegmentedButtonRow(
224+
Column(
178225
modifier = Modifier
179226
.fillMaxWidth()
180-
.padding(16.dp)
181227
.align(Alignment.BottomCenter),
228+
horizontalAlignment = Alignment.End
182229
) {
183-
SegmentedButton(
184-
selected = tab.selectedChoice == 0,
185-
onClick = {
186-
tab.selectedChoice = 0
187-
},
188-
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3),
189-
label = {
190-
Text(text = stringResource(R.string.user_apps))
230+
AnimatedVisibility(!tab.isSearchPanelOpen) {
231+
FloatingActionButton(
232+
modifier = Modifier.padding(horizontal = 16.dp),
233+
onClick = {
234+
tab.isSearchPanelOpen = true
235+
}
236+
) {
237+
Icon(
238+
imageVector = Icons.Rounded.Search,
239+
contentDescription = null
240+
)
191241
}
192-
)
242+
}
193243

194-
SegmentedButton(
195-
selected = tab.selectedChoice == 1,
196-
onClick = {
197-
tab.selectedChoice = 1
198-
},
199-
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3),
200-
label = {
201-
Text(text = stringResource(R.string.system_apps))
202-
}
203-
)
244+
SingleChoiceSegmentedButtonRow(
245+
modifier = Modifier
246+
.fillMaxWidth()
247+
.padding(16.dp)
248+
) {
249+
SegmentedButton(
250+
selected = tab.selectedChoice == 0,
251+
onClick = {
252+
tab.selectedChoice = 0
253+
},
254+
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3),
255+
label = {
256+
Text(text = stringResource(R.string.user_apps))
257+
}
258+
)
259+
260+
SegmentedButton(
261+
selected = tab.selectedChoice == 1,
262+
onClick = {
263+
tab.selectedChoice = 1
264+
},
265+
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3),
266+
label = {
267+
Text(text = stringResource(R.string.system_apps))
268+
}
269+
)
270+
271+
SegmentedButton(
272+
selected = tab.selectedChoice == 2,
273+
onClick = {
274+
tab.selectedChoice = 2
275+
},
276+
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3),
277+
label = {
278+
Text(text = stringResource(R.string.all))
279+
}
280+
)
281+
}
204282

205-
SegmentedButton(
206-
selected = tab.selectedChoice == 2,
207-
onClick = {
208-
tab.selectedChoice = 2
209-
},
210-
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3),
211-
label = {
212-
Text(text = stringResource(R.string.all))
283+
AnimatedVisibility(tab.isSearchPanelOpen) {
284+
Row(
285+
Modifier
286+
.fillMaxWidth()
287+
.padding(horizontal = 16.dp)
288+
.padding(bottom = 16.dp)
289+
.height(56.dp)
290+
.block(
291+
borderSize = 0.dp,
292+
shape = CircleShape
293+
),
294+
verticalAlignment = Alignment.CenterVertically
295+
) {
296+
TextField(
297+
modifier = Modifier
298+
.fillMaxWidth(),
299+
colors = TextFieldDefaults.colors(
300+
focusedContainerColor = AlertDialogDefaults.containerColor,
301+
unfocusedContainerColor = AlertDialogDefaults.containerColor,
302+
disabledContainerColor = AlertDialogDefaults.containerColor,
303+
focusedIndicatorColor = Color.Transparent,
304+
unfocusedIndicatorColor = Color.Transparent,
305+
),
306+
value = tab.searchQuery,
307+
onValueChange = {
308+
tab.searchQuery = it
309+
},
310+
placeholder = {
311+
Text(
312+
modifier = Modifier.alpha(0.75f),
313+
text = stringResource(R.string.search_query),
314+
)
315+
},
316+
leadingIcon = {
317+
IconButton(onClick = { tab.isSearchPanelOpen = false }) {
318+
Icon(
319+
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
320+
contentDescription = null
321+
)
322+
}
323+
},
324+
trailingIcon = {
325+
AnimatedVisibility(visible = tab.searchQuery.isNotEmpty()) {
326+
IconButton(
327+
onClick = {
328+
if (tab.isSearching) {
329+
tab.isSearching = false
330+
} else {
331+
tab.searchQuery = emptyString
332+
tab.isSearching = false
333+
}
334+
}
335+
) {
336+
Icon(
337+
imageVector = if (tab.isSearching) Icons.Rounded.Pause
338+
else Icons.Rounded.Cancel, contentDescription = null
339+
)
340+
}
341+
}
342+
},
343+
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
344+
keyboardActions = KeyboardActions(
345+
onSearch = {
346+
CoroutineScope(Dispatchers.IO).launch {
347+
if (tab.isSearching) {
348+
tab.isSearching = false
349+
delay(200)
350+
}
351+
352+
tab.isSearching = true
353+
354+
val list = arrayListOf<AppHolder>().apply {
355+
when (tab.selectedChoice) {
356+
0 -> addAll(tab.userApps)
357+
1 -> addAll(tab.systemApps)
358+
else -> addAll(tab.userApps + tab.systemApps)
359+
}
360+
}
361+
362+
tab.appsList.clear()
363+
tab.appsList.addAll(list.filter {
364+
it.name.contains(tab.searchQuery, true)
365+
})
366+
367+
tab.isSearching = false
368+
}
369+
}
370+
)
371+
)
213372
}
214-
)
373+
}
215374
}
216375

217376
androidx.compose.animation.AnimatedVisibility(
218377
modifier = Modifier.align(Alignment.Center),
219-
visible = tab.isLoading
378+
visible = tab.isLoading || tab.isSearching
220379
) {
221380
CircularProgressIndicator(
222381
modifier = Modifier.align(Alignment.Center)

0 commit comments

Comments
 (0)