Skip to content

Commit dad1316

Browse files
committed
fixed an issue with filtering logs
1 parent 42906e7 commit dad1316

5 files changed

Lines changed: 124 additions & 71 deletions

File tree

app/src/main/java/in/hridayan/ashell/commandexamples/presentation/component/chip/LabelChip.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ fun LabelChip(
6262
crossIconOnClick(label)
6363
},
6464
colors = IconButtonDefaults.iconButtonColors(
65-
containerColor = MaterialTheme.colorScheme.errorContainer,
66-
contentColor = MaterialTheme.colorScheme.onErrorContainer
65+
containerColor = MaterialTheme.colorScheme.error,
66+
contentColor = MaterialTheme.colorScheme.onError
6767
),
6868
modifier = Modifier.size(20.dp)
6969
) {

app/src/main/java/in/hridayan/ashell/qstiles/presentation/model/TileDashBoardScreenUiState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ data class TileDashBoardScreenUiState(
1313
val logs: List<TileLog> = emptyList(),
1414
val totalExecutions: Int = 0,
1515
val successRate: String = "0.0%",
16-
val logsSearchQuery: String = ""
16+
val selectedTileIdFilter: Int? = null,
17+
val tilesWithLogs: List<TileConfig> = emptyList()
1718
)

app/src/main/java/in/hridayan/ashell/qstiles/presentation/screen/TileDashBoardScreen.kt

Lines changed: 105 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ package `in`.hridayan.ashell.qstiles.presentation.screen
88

99
import android.content.pm.PackageManager
1010
import androidx.compose.animation.AnimatedContent
11+
import androidx.compose.animation.AnimatedVisibility
1112
import androidx.compose.animation.Crossfade
1213
import androidx.compose.animation.animateContentSize
14+
import androidx.compose.animation.expandVertically
15+
import androidx.compose.animation.fadeIn
16+
import androidx.compose.animation.fadeOut
17+
import androidx.compose.animation.shrinkVertically
1318
import androidx.compose.animation.togetherWith
1419
import androidx.compose.foundation.background
1520
import androidx.compose.foundation.basicMarquee
@@ -33,11 +38,12 @@ import androidx.compose.foundation.lazy.items
3338
import androidx.compose.foundation.lazy.rememberLazyListState
3439
import androidx.compose.foundation.shape.CircleShape
3540
import androidx.compose.foundation.shape.RoundedCornerShape
36-
import androidx.compose.foundation.text.BasicTextField
3741
import androidx.compose.material3.Button
3842
import androidx.compose.material3.ButtonDefaults
3943
import androidx.compose.material3.Card
4044
import androidx.compose.material3.CardDefaults
45+
import androidx.compose.material3.DropdownMenu
46+
import androidx.compose.material3.DropdownMenuItem
4147
import androidx.compose.material3.ExperimentalMaterial3Api
4248
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
4349
import androidx.compose.material3.Icon
@@ -59,7 +65,6 @@ import androidx.compose.ui.Alignment
5965
import androidx.compose.ui.Modifier
6066
import androidx.compose.ui.draw.clip
6167
import androidx.compose.ui.graphics.Color
62-
import androidx.compose.ui.graphics.SolidColor
6368
import androidx.compose.ui.graphics.painter.Painter
6469
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
6570
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -77,13 +82,14 @@ import androidx.lifecycle.LifecycleEventObserver
7782
import androidx.lifecycle.compose.LocalLifecycleOwner
7883
import androidx.navigation.NavController
7984
import `in`.hridayan.ashell.R
85+
import `in`.hridayan.ashell.commandexamples.presentation.component.chip.LabelChip
8086
import `in`.hridayan.ashell.core.common.LocalDarkMode
8187
import `in`.hridayan.ashell.core.common.LocalWeakHaptic
8288
import `in`.hridayan.ashell.core.common.constants.SHIZUKU_PACKAGE_NAME
8389
import `in`.hridayan.ashell.core.common.constants.UrlConst
8490
import `in`.hridayan.ashell.core.presentation.components.card.IconWithTextCard
85-
import `in`.hridayan.ashell.core.presentation.components.modifier.dashedBorder
8691
import `in`.hridayan.ashell.core.presentation.components.haptic.withHaptic
92+
import `in`.hridayan.ashell.core.presentation.components.modifier.dashedBorder
8793
import `in`.hridayan.ashell.core.presentation.components.navigation.FloatingNavPill
8894
import `in`.hridayan.ashell.core.presentation.components.navigation.FloatingNavPillDefaults
8995
import `in`.hridayan.ashell.core.presentation.components.navigation.FloatingNavPillItem
@@ -101,6 +107,7 @@ import `in`.hridayan.ashell.navigation.slideFadeInFromRight
101107
import `in`.hridayan.ashell.navigation.slideFadeOutToLeft
102108
import `in`.hridayan.ashell.navigation.slideFadeOutToRight
103109
import `in`.hridayan.ashell.qstiles.data.provider.TileIconProvider
110+
import `in`.hridayan.ashell.qstiles.domain.model.TileConfig
104111
import `in`.hridayan.ashell.qstiles.domain.model.TileExecutionMode
105112
import `in`.hridayan.ashell.qstiles.domain.model.TileLog
106113
import `in`.hridayan.ashell.qstiles.presentation.model.TileDashBoardScreenUiState
@@ -277,11 +284,14 @@ private fun TilesContent(
277284
state = listState,
278285
contentPadding = innerPadding
279286
) {
280-
281287
item { Spacer(Modifier.height(25.dp)) }
282288

283-
if (!hasNotificationAccess) {
284-
item {
289+
item {
290+
AnimatedVisibility(
291+
visible = !hasNotificationAccess,
292+
enter = fadeIn() + expandVertically(),
293+
exit = fadeOut() + shrinkVertically(),
294+
) {
285295
NotificationAccessRequestCard(
286296
modifier = Modifier
287297
.fillMaxWidth()
@@ -291,8 +301,12 @@ private fun TilesContent(
291301
}
292302
}
293303

294-
if (showShizukuUnavailableCard) {
295-
item {
304+
item {
305+
AnimatedVisibility(
306+
visible = showShizukuUnavailableCard,
307+
enter = fadeIn() + expandVertically(),
308+
exit = fadeOut() + shrinkVertically(),
309+
) {
296310
ShizukuUnavailableCard(
297311
modifier = Modifier
298312
.fillMaxWidth()
@@ -308,9 +322,13 @@ private fun TilesContent(
308322
}
309323
}
310324

311-
if (Shizuku.pingBinder()) {
312-
if (!shizukuPermissionGranted.value) {
313-
item {
325+
item {
326+
if (Shizuku.pingBinder()) {
327+
AnimatedVisibility(
328+
visible = !shizukuPermissionGranted.value,
329+
enter = fadeIn() + expandVertically(),
330+
exit = fadeOut() + shrinkVertically(),
331+
) {
314332
ShizukuPermissionRequestCard(
315333
modifier = Modifier
316334
.fillMaxWidth()
@@ -413,8 +431,9 @@ private fun LogsContent(
413431
item {
414432
RecentActivityHeader(
415433
modifier = Modifier.padding(20.dp),
416-
searchQuery = uiState.logsSearchQuery,
417-
onSearchQueryChange = viewModel::onLogsSearchQueryChange
434+
selectedTileId = uiState.selectedTileIdFilter,
435+
tilesWithLogs = uiState.tilesWithLogs,
436+
onFilterChange = viewModel::onFilterChange
418437
)
419438
}
420439

@@ -620,11 +639,15 @@ private fun StatCard(
620639
@Composable
621640
private fun RecentActivityHeader(
622641
modifier: Modifier = Modifier,
623-
searchQuery: String,
624-
onSearchQueryChange: (String) -> Unit
642+
selectedTileId: Int?,
643+
tilesWithLogs: List<TileConfig>,
644+
onFilterChange: (Int?) -> Unit
625645
) {
626-
var isSearchExpanded by remember { mutableStateOf(false) }
646+
var isMenuExpanded by remember { mutableStateOf(false) }
627647
val weakHaptic = LocalWeakHaptic.current
648+
val selectedTile = remember(selectedTileId, tilesWithLogs) {
649+
tilesWithLogs.find { it.id == selectedTileId }
650+
}
628651

629652
Row(
630653
modifier = modifier
@@ -633,26 +656,13 @@ private fun RecentActivityHeader(
633656
verticalAlignment = Alignment.CenterVertically,
634657
horizontalArrangement = Arrangement.SpaceBetween
635658
) {
636-
Crossfade(targetState = isSearchExpanded) { expanded ->
637-
if (expanded) {
638-
BasicTextField(
639-
modifier = Modifier
640-
.weight(1f)
641-
.padding(end = 10.dp)
642-
.background(MaterialTheme.colorScheme.surfaceContainer, CircleShape)
643-
.padding(horizontal = 16.dp, vertical = 8.dp),
644-
value = searchQuery,
645-
onValueChange = onSearchQueryChange,
646-
textStyle = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface),
647-
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
648-
decorationBox = { innerTextField ->
649-
if (searchQuery.isEmpty()) {
650-
AutoResizeableText(
651-
text = stringResource(R.string.search_tile),
652-
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
653-
)
654-
}
655-
innerTextField()
659+
Crossfade(targetState = selectedTile) { tile ->
660+
if (tile != null) {
661+
LabelChip(
662+
label = tile.name,
663+
showCrossIcon = true,
664+
crossIconOnClick = {
665+
onFilterChange(null)
656666
}
657667
)
658668
} else {
@@ -664,30 +674,71 @@ private fun RecentActivityHeader(
664674
}
665675
}
666676

667-
Row(
668-
modifier = Modifier
669-
.clip(CircleShape)
670-
.clickable {
671-
isSearchExpanded = !isSearchExpanded
672-
weakHaptic()
673-
}
674-
.padding(horizontal = 12.dp, vertical = 6.dp),
675-
verticalAlignment = Alignment.CenterVertically,
676-
horizontalArrangement = Arrangement.spacedBy(6.dp)
677-
) {
678-
Icon(
679-
modifier = Modifier.size(18.dp),
680-
painter = painterResource(if (isSearchExpanded) R.drawable.ts_close else R.drawable.ic_filter_alt),
681-
contentDescription = null,
682-
tint = MaterialTheme.colorScheme.primary
683-
)
684-
if (!isSearchExpanded) {
677+
Box {
678+
Row(
679+
modifier = Modifier
680+
.clip(CircleShape)
681+
.clickable {
682+
isMenuExpanded = !isMenuExpanded
683+
weakHaptic()
684+
}
685+
.padding(horizontal = 12.dp, vertical = 6.dp),
686+
verticalAlignment = Alignment.CenterVertically,
687+
horizontalArrangement = Arrangement.spacedBy(6.dp)
688+
) {
689+
Icon(
690+
modifier = Modifier.size(18.dp),
691+
painter = painterResource(R.drawable.ic_filter_alt),
692+
contentDescription = null,
693+
tint = MaterialTheme.colorScheme.primary
694+
)
685695
Text(
686696
text = stringResource(R.string.filter),
687697
style = MaterialTheme.typography.labelLarge,
688698
color = MaterialTheme.colorScheme.primary
689699
)
690700
}
701+
702+
DropdownMenu(
703+
expanded = isMenuExpanded,
704+
onDismissRequest = { isMenuExpanded = false },
705+
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceContainerHigh)
706+
) {
707+
DropdownMenuItem(
708+
text = { Text(stringResource(R.string.all)) },
709+
onClick = {
710+
onFilterChange(null)
711+
isMenuExpanded = false
712+
weakHaptic()
713+
}
714+
)
715+
tilesWithLogs.forEach { tile ->
716+
DropdownMenuItem(
717+
text = {
718+
Row(
719+
verticalAlignment = Alignment.CenterVertically,
720+
horizontalArrangement = Arrangement.spacedBy(8.dp)
721+
) {
722+
val tileIcon = TileIconProvider.iconById[tile.iconId]
723+
Icon(
724+
modifier = Modifier.size(18.dp),
725+
painter = painterResource(
726+
tileIcon?.resId ?: R.drawable.ic_adb
727+
),
728+
contentDescription = null,
729+
tint = MaterialTheme.colorScheme.primary
730+
)
731+
Text(tile.name)
732+
}
733+
},
734+
onClick = {
735+
onFilterChange(tile.id)
736+
isMenuExpanded = false
737+
weakHaptic()
738+
}
739+
)
740+
}
741+
}
691742
}
692743
}
693744
}

app/src/main/java/in/hridayan/ashell/qstiles/presentation/viewmodel/TileDashBoardViewModel.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class TileDashboardViewModel @Inject constructor(
2929
) : ViewModel() {
3030

3131
private val _currentTab = MutableStateFlow(TileScreenTabs.TILES)
32-
private val _logsSearchQuery = MutableStateFlow("")
32+
private val _selectedTileIdFilter = MutableStateFlow<Int?>(null)
3333

3434
val state: StateFlow<TileDashBoardScreenUiState> =
3535
combine(
@@ -40,25 +40,24 @@ class TileDashboardViewModel @Inject constructor(
4040
logRepository.getTotalExecutions().distinctUntilChanged(),
4141
logRepository.getSuccessCount().distinctUntilChanged(),
4242
_currentTab,
43-
_logsSearchQuery
44-
) { logs, total, success, tab, query ->
45-
DataBundle(logs, total, success, tab, query)
43+
_selectedTileIdFilter
44+
) { logs, total, success, tab, filterId ->
45+
DataBundle(logs, total, success, tab, filterId)
4646
}
4747
) { tiles, running, bundle ->
4848

4949
val logs = bundle.logs
5050
val total = bundle.total
5151
val success = bundle.success
5252
val tab = bundle.tab
53-
val query = bundle.query
53+
val filterId = bundle.filterId
54+
val logTileIds = logs.map { it.tileId }.toSet()
55+
val tilesWithLogs = tiles.filter { it.id in logTileIds }
5456

55-
val filteredLogs = if (query.isBlank()) {
57+
val filteredLogs = if (filterId == null) {
5658
logs
5759
} else {
58-
logs.filter { log ->
59-
val tileName = tiles.find { it.id == log.tileId }?.name ?: "Unknown"
60-
tileName.contains(query, ignoreCase = true)
61-
}
60+
logs.filter { it.tileId == filterId }
6261
}
6362

6463
val successRateStr = if (total > 0) {
@@ -75,7 +74,8 @@ class TileDashboardViewModel @Inject constructor(
7574
logs = filteredLogs,
7675
totalExecutions = total,
7776
successRate = successRateStr,
78-
logsSearchQuery = query
77+
selectedTileIdFilter = filterId,
78+
tilesWithLogs = tilesWithLogs
7979
)
8080
}
8181
.stateIn(
@@ -88,15 +88,15 @@ class TileDashboardViewModel @Inject constructor(
8888
_currentTab.value = index
8989
}
9090

91-
fun onLogsSearchQueryChange(query: String) {
92-
_logsSearchQuery.value = query
91+
fun onFilterChange(tileId: Int?) {
92+
_selectedTileIdFilter.value = tileId
9393
}
9494

9595
private data class DataBundle(
9696
val logs: List<TileLog>,
9797
val total: Int,
9898
val success: Int,
9999
val tab: Int,
100-
val query: String
100+
val filterId: Int?
101101
)
102102
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<string name="adb_via_wireless_debugging">ADB via Wireless Debugging</string>
1515
<string name="adb_via_wireless_debugging_summary">Connect devices (Android 11+) over Wi-Fi using wireless debugging and execute ADB commands remotely.</string>
1616
<string name="add">Add</string>
17+
<string name="all">All</string>
1718
<string name="tiles">Tiles</string>
1819
<string name="recent_activity">Recent activity</string>
1920
<string name="total_executions">Total executions</string>

0 commit comments

Comments
 (0)