Fix Debug panel: Decoded payload not showing (#2555)

This commit is contained in:
DaneEvans
2025-07-29 19:13:06 +10:00
committed by GitHub
parent ceabafb545
commit c1408816a4

View File

@@ -99,9 +99,7 @@ private val REGEX_ANNOTATED_NODE_ID = Regex("\\(![0-9a-fA-F]{8}\\)$", RegexOptio
@Suppress("LongMethod")
@Composable
internal fun DebugScreen(
viewModel: DebugViewModel = hiltViewModel(),
) {
internal fun DebugScreen(viewModel: DebugViewModel = hiltViewModel()) {
val listState = rememberLazyListState()
val logs by viewModel.meshLog.collectAsStateWithLifecycle()
val searchState by viewModel.searchState.collectAsStateWithLifecycle()
@@ -111,13 +109,12 @@ internal fun DebugScreen(
var filterMode by remember { mutableStateOf(FilterMode.OR) }
// Use the new filterLogs method to include decodedPayload in filtering
val filteredLogs = remember(logs, filterTexts, filterMode) {
viewModel.filterManager.filterLogs(logs, filterTexts, filterMode).toImmutableList()
}
val filteredLogs =
remember(logs, filterTexts, filterMode) {
viewModel.filterManager.filterLogs(logs, filterTexts, filterMode).toImmutableList()
}
LaunchedEffect(filteredLogs) {
viewModel.updateFilteredLogs(filteredLogs)
}
LaunchedEffect(filteredLogs) { viewModel.updateFilteredLogs(filteredLogs) }
val shouldAutoScroll by remember { derivedStateOf { listState.firstVisibleItemIndex < 3 } }
if (shouldAutoScroll) {
@@ -133,28 +130,19 @@ internal fun DebugScreen(
listState.requestScrollToItem(searchState.allMatches[searchState.currentMatchIndex].logIndex)
}
}
Column(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
) {
Column(modifier = Modifier.fillMaxSize()) {
LazyColumn(modifier = Modifier.fillMaxSize(), state = listState) {
stickyHeader {
val animatedAlpha by animateFloatAsState(
targetValue = if (!listState.isScrollInProgress) 1.0f else 0f,
label = "alpha"
)
val animatedAlpha by
animateFloatAsState(targetValue = if (!listState.isScrollInProgress) 1.0f else 0f, label = "alpha")
DebugSearchStateviewModelDefaults(
modifier = Modifier.graphicsLayer(
alpha = animatedAlpha
),
modifier = Modifier.graphicsLayer(alpha = animatedAlpha),
searchState = searchState,
filterTexts = filterTexts,
presetFilters = viewModel.presetFilters,
logs = logs,
filterMode = filterMode,
onFilterModeChange = { filterMode = it }
onFilterModeChange = { filterMode = it },
)
}
items(filteredLogs, key = { it.uuid }) { log ->
@@ -163,9 +151,7 @@ internal fun DebugScreen(
log = log,
searchText = searchState.searchText,
isSelected = selectedLogId == log.uuid,
onLogClick = {
viewModel.setSelectedLogId(if (selectedLogId == log.uuid) null else log.uuid)
}
onLogClick = { viewModel.setSelectedLogId(if (selectedLogId == log.uuid) null else log.uuid) },
)
}
}
@@ -178,49 +164,43 @@ internal fun DebugItem(
modifier: Modifier = Modifier,
searchText: String = "",
isSelected: Boolean = false,
onLogClick: () -> Unit = {}
onLogClick: () -> Unit = {},
) {
val colorScheme = MaterialTheme.colorScheme
Card(
modifier = modifier
.fillMaxWidth()
.padding(4.dp),
colors = CardDefaults.cardColors(
containerColor = if (isSelected) {
modifier = modifier.fillMaxWidth().padding(4.dp),
colors =
CardDefaults.cardColors(
containerColor =
if (isSelected) {
colorScheme.primary.copy(alpha = 0.1f)
} else {
colorScheme.surface
}
},
),
border = if (isSelected) {
border =
if (isSelected) {
BorderStroke(2.dp, colorScheme.primary)
} else {
null
}
},
) {
SelectionContainer {
Column(
modifier = Modifier
.padding(if (isSelected) 12.dp else 8.dp)
.fillMaxWidth()
.clickable { onLogClick() }
modifier = Modifier.padding(if (isSelected) 12.dp else 8.dp).fillMaxWidth().clickable { onLogClick() },
) {
DebugItemHeader(
log = log,
searchText = searchText,
isSelected = isSelected,
theme = colorScheme
)
DebugItemHeader(log = log, searchText = searchText, isSelected = isSelected, theme = colorScheme)
val messageAnnotatedString = rememberAnnotatedLogMessage(log, searchText)
Text(
text = messageAnnotatedString,
softWrap = false,
style = TextStyle(
style =
TextStyle(
fontSize = if (isSelected) 12.sp else 9.sp,
fontFamily = FontFamily.Monospace,
color = colorScheme.onSurface
)
color = colorScheme.onSurface,
),
)
// Show decoded payload if available, with search highlighting
if (!log.decodedPayload.isNullOrBlank()) {
@@ -229,7 +209,7 @@ internal fun DebugItem(
isSelected = isSelected,
colorScheme = colorScheme,
searchText = searchText,
modifier = Modifier.weight(1f)
modifier = Modifier,
)
}
}
@@ -238,79 +218,60 @@ internal fun DebugItem(
}
@Composable
private fun DebugItemHeader(
log: UiMeshLog,
searchText: String,
isSelected: Boolean,
theme: ColorScheme
) {
private fun DebugItemHeader(log: UiMeshLog, searchText: String, isSelected: Boolean, theme: ColorScheme) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = if (isSelected) 12.dp else 8.dp),
modifier = Modifier.fillMaxWidth().padding(bottom = if (isSelected) 12.dp else 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
val typeAnnotatedString = rememberAnnotatedString(
text = log.messageType,
searchText = searchText
)
val typeAnnotatedString = rememberAnnotatedString(text = log.messageType, searchText = searchText)
Text(
text = typeAnnotatedString,
modifier = Modifier.weight(1f),
style = TextStyle(
style =
TextStyle(
fontWeight = FontWeight.Bold,
fontSize = if (isSelected) 16.sp else 14.sp,
color = theme.onSurface
color = theme.onSurface,
),
)
// Copy full log: message + decoded payload if present
val fullLogText = remember(log.logMessage, log.decodedPayload) {
buildString {
append(log.logMessage)
if (!log.decodedPayload.isNullOrBlank()) {
append("\n\nDecoded Payload:\n{")
append("\n")
append(log.decodedPayload)
append("\n}")
val fullLogText =
remember(log.logMessage, log.decodedPayload) {
buildString {
append(log.logMessage)
if (!log.decodedPayload.isNullOrBlank()) {
append("\n\nDecoded Payload:\n{")
append("\n")
append(log.decodedPayload)
append("\n}")
}
}
}
}
CopyIconButton(
valueToCopy = fullLogText,
modifier = Modifier.padding(start = 8.dp)
)
CopyIconButton(valueToCopy = fullLogText, modifier = Modifier.padding(start = 8.dp))
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = stringResource(id = R.string.logs),
tint = Color.Gray.copy(alpha = 0.6f),
modifier = Modifier.padding(end = 8.dp),
)
val dateAnnotatedString = rememberAnnotatedString(
text = log.formattedReceivedDate,
searchText = searchText
)
val dateAnnotatedString = rememberAnnotatedString(text = log.formattedReceivedDate, searchText = searchText)
Text(
text = dateAnnotatedString,
style = TextStyle(
style =
TextStyle(
fontWeight = FontWeight.Bold,
fontSize = if (isSelected) 14.sp else 12.sp,
color = theme.onSurface
color = theme.onSurface,
),
)
}
}
@Composable
private fun rememberAnnotatedString(
text: String,
searchText: String
): AnnotatedString {
private fun rememberAnnotatedString(text: String, searchText: String): AnnotatedString {
val theme = MaterialTheme.colorScheme
val highlightStyle = SpanStyle(
background = theme.primary.copy(alpha = 0.3f),
color = theme.onSurface
)
val highlightStyle = SpanStyle(background = theme.primary.copy(alpha = 0.3f), color = theme.onSurface)
return remember(text, searchText) {
buildAnnotatedString {
@@ -318,11 +279,7 @@ private fun rememberAnnotatedString(
if (searchText.isNotEmpty()) {
searchText.split(" ").forEach { term ->
Regex(Regex.escape(term), RegexOption.IGNORE_CASE).findAll(text).forEach { match ->
addStyle(
style = highlightStyle,
start = match.range.first,
end = match.range.last + 1
)
addStyle(style = highlightStyle, start = match.range.first, end = match.range.last + 1)
}
}
}
@@ -333,38 +290,23 @@ private fun rememberAnnotatedString(
@Composable
private fun rememberAnnotatedLogMessage(log: UiMeshLog, searchText: String): AnnotatedString {
val theme = MaterialTheme.colorScheme
val style = SpanStyle(
color = colorResource(id = R.color.colorAnnotation),
fontStyle = FontStyle.Italic,
)
val highlightStyle = SpanStyle(
background = theme.primary.copy(alpha = 0.3f),
color = theme.onSurface
)
val style = SpanStyle(color = colorResource(id = R.color.colorAnnotation), fontStyle = FontStyle.Italic)
val highlightStyle = SpanStyle(background = theme.primary.copy(alpha = 0.3f), color = theme.onSurface)
return remember(log.uuid, searchText) {
buildAnnotatedString {
append(log.logMessage)
// Add node ID annotations
REGEX_ANNOTATED_NODE_ID.findAll(log.logMessage).toList().reversed()
.forEach {
addStyle(
style = style,
start = it.range.first,
end = it.range.last + 1
)
}
REGEX_ANNOTATED_NODE_ID.findAll(log.logMessage).toList().reversed().forEach {
addStyle(style = style, start = it.range.first, end = it.range.last + 1)
}
// Add search highlight annotations
if (searchText.isNotEmpty()) {
searchText.split(" ").forEach { term ->
Regex(Regex.escape(term), RegexOption.IGNORE_CASE).findAll(log.logMessage).forEach { match ->
addStyle(
style = highlightStyle,
start = match.range.first,
end = match.range.last + 1
)
addStyle(style = highlightStyle, start = match.range.first, end = match.range.last + 1)
}
}
}
@@ -381,22 +323,23 @@ private fun DebugPacketPreview() {
uuid = "",
messageType = "NodeInfo",
formattedReceivedDate = "9/27/20, 8:00:58 PM",
logMessage = "from: 2885173132\n" +
"decoded {\n" +
" position {\n" +
" altitude: 60\n" +
" battery_level: 81\n" +
" latitude_i: 411111136\n" +
" longitude_i: -711111805\n" +
" time: 1600390966\n" +
" }\n" +
"}\n" +
"hop_limit: 3\n" +
"id: 1737414295\n" +
"rx_snr: 9.5\n" +
"rx_time: 316400569\n" +
"to: -1409790708",
)
logMessage =
"from: 2885173132\n" +
"decoded {\n" +
" position {\n" +
" altitude: 60\n" +
" battery_level: 81\n" +
" latitude_i: 411111136\n" +
" longitude_i: -711111805\n" +
" time: 1600390966\n" +
" }\n" +
"}\n" +
"hop_limit: 3\n" +
"id: 1737414295\n" +
"rx_snr: 9.5\n" +
"rx_time: 316400569\n" +
"to: -1409790708",
),
)
}
}
@@ -410,9 +353,9 @@ private fun DebugItemWithSearchHighlightPreview() {
uuid = "1",
messageType = "TextMessage",
formattedReceivedDate = "9/27/20, 8:00:58 PM",
logMessage = "Hello world! This is a test message with some keywords to search for."
logMessage = "Hello world! This is a test message with some keywords to search for.",
),
searchText = "test message"
searchText = "test message",
)
}
}
@@ -426,8 +369,8 @@ private fun DebugItemPositionPreview() {
uuid = "2",
messageType = "Position",
formattedReceivedDate = "9/27/20, 8:01:15 PM",
logMessage = "Position update from node (!a1b2c3d4) at coordinates 40.7128, -74.0060"
)
logMessage = "Position update from node (!a1b2c3d4) at coordinates 40.7128, -74.0060",
),
)
}
}
@@ -441,10 +384,11 @@ private fun DebugItemErrorPreview() {
uuid = "3",
messageType = "Error",
formattedReceivedDate = "9/27/20, 8:02:30 PM",
logMessage = "Connection failed: timeout after 30 seconds\n" +
"Retry attempt: 3/5\n" +
"Last known position: 40.7128, -74.0060"
)
logMessage =
"Connection failed: timeout after 30 seconds\n" +
"Retry attempt: 3/5\n" +
"Last known position: 40.7128, -74.0060",
),
)
}
}
@@ -458,16 +402,17 @@ private fun DebugItemLongMessagePreview() {
uuid = "4",
messageType = "Waypoint",
formattedReceivedDate = "9/27/20, 8:03:45 PM",
logMessage = "Waypoint created:\n" +
" Name: Home Base\n" +
" Description: Primary meeting location\n" +
" Latitude: 40.7128\n" +
" Longitude: -74.0060\n" +
" Altitude: 100m\n" +
" Icon: 🏠\n" +
" Created by: (!a1b2c3d4)\n" +
" Expires: 2025-12-31 23:59:59"
)
logMessage =
"Waypoint created:\n" +
" Name: Home Base\n" +
" Description: Primary meeting location\n" +
" Latitude: 40.7128\n" +
" Longitude: -74.0060\n" +
" Altitude: 100m\n" +
" Icon: 🏠\n" +
" Created by: (!a1b2c3d4)\n" +
" Expires: 2025-12-31 23:59:59",
),
)
}
}
@@ -481,9 +426,9 @@ private fun DebugItemSelectedPreview() {
uuid = "5",
messageType = "TextMessage",
formattedReceivedDate = "9/27/20, 8:04:20 PM",
logMessage = "This is a selected log item with larger font sizes for better readability."
logMessage = "This is a selected log item with larger font sizes for better readability.",
),
isSelected = true
isSelected = true,
)
}
}
@@ -492,26 +437,15 @@ private fun DebugItemSelectedPreview() {
@Composable
private fun DebugMenuActionsPreview() {
AppTheme {
Row(
modifier = Modifier.padding(16.dp)
) {
IconButton(
onClick = { /* Preview only */ },
modifier = Modifier.padding(4.dp)
) {
Row(modifier = Modifier.padding(16.dp)) {
IconButton(onClick = { /* Preview only */ }, modifier = Modifier.padding(4.dp)) {
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = stringResource(id = R.string.debug_logs_export)
contentDescription = stringResource(id = R.string.debug_logs_export),
)
}
IconButton(
onClick = { /* Preview only */ },
modifier = Modifier.padding(4.dp)
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(id = R.string.debug_clear)
)
IconButton(onClick = { /* Preview only */ }, modifier = Modifier.padding(4.dp)) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(id = R.string.debug_clear))
}
}
}
@@ -523,50 +457,33 @@ private fun DebugMenuActionsPreview() {
private fun DebugScreenEmptyPreview() {
AppTheme {
Surface {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
stickyHeader {
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Column(
modifier = Modifier.padding(8.dp)
) {
Surface(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
Column(modifier = Modifier.padding(8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
) {
OutlinedTextField(
value = "",
onValueChange = { },
modifier = Modifier
.weight(1f)
.padding(end = 8.dp),
onValueChange = {},
modifier = Modifier.weight(1f).padding(end = 8.dp),
placeholder = { Text("Search in logs...") },
singleLine = true
singleLine = true,
)
TextButton(
onClick = { }
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Filters",
style = TextStyle(fontWeight = FontWeight.Bold)
)
TextButton(onClick = {}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Filters", style = TextStyle(fontWeight = FontWeight.Bold))
Icon(
imageVector = Icons.TwoTone.FilterAltOff,
contentDescription = stringResource(id = R.string.debug_filters)
contentDescription = stringResource(id = R.string.debug_filters),
)
}
}
@@ -577,29 +494,16 @@ private fun DebugScreenEmptyPreview() {
}
// Empty state
item {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(32.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(modifier = Modifier.fillMaxWidth().padding(32.dp), contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "No Debug Logs",
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
style = TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Bold),
)
Text(
text = "Debug logs will appear here when available",
style = TextStyle(
fontSize = 14.sp,
color = Color.Gray
),
modifier = Modifier.padding(top = 8.dp)
style = TextStyle(fontSize = 14.sp, color = Color.Gray),
modifier = Modifier.padding(top = 8.dp),
)
}
}
@@ -614,12 +518,14 @@ private fun DebugScreenEmptyPreview() {
@Suppress("detekt:LongMethod") // big preview
private fun DebugScreenWithSampleDataPreview() {
AppTheme {
val sampleLogs = listOf(
UiMeshLog(
uuid = "1",
messageType = "NodeInfo",
formattedReceivedDate = "9/27/20, 8:00:58 PM",
logMessage = "from: 2885173132\n" +
val sampleLogs =
listOf(
UiMeshLog(
uuid = "1",
messageType = "NodeInfo",
formattedReceivedDate = "9/27/20, 8:00:58 PM",
logMessage =
"from: 2885173132\n" +
"decoded {\n" +
" position {\n" +
" altitude: 60\n" +
@@ -633,100 +539,74 @@ private fun DebugScreenWithSampleDataPreview() {
"id: 1737414295\n" +
"rx_snr: 9.5\n" +
"rx_time: 316400569\n" +
"to: -1409790708"
),
UiMeshLog(
uuid = "2",
messageType = "TextMessage",
formattedReceivedDate = "9/27/20, 8:01:15 PM",
logMessage = "Hello from node (!a1b2c3d4)! How's the weather today?"
),
UiMeshLog(
uuid = "3",
messageType = "Position",
formattedReceivedDate = "9/27/20, 8:02:30 PM",
logMessage = "Position update: 40.7128, -74.0060, altitude: 100m, battery: 85%"
),
UiMeshLog(
uuid = "4",
messageType = "Waypoint",
formattedReceivedDate = "9/27/20, 8:03:45 PM",
logMessage = "New waypoint created: 'Meeting Point' at 40.7589, -73.9851"
),
UiMeshLog(
uuid = "5",
messageType = "Error",
formattedReceivedDate = "9/27/20, 8:04:20 PM",
logMessage = "Connection timeout - retrying in 5 seconds..."
"to: -1409790708",
),
UiMeshLog(
uuid = "2",
messageType = "TextMessage",
formattedReceivedDate = "9/27/20, 8:01:15 PM",
logMessage = "Hello from node (!a1b2c3d4)! How's the weather today?",
),
UiMeshLog(
uuid = "3",
messageType = "Position",
formattedReceivedDate = "9/27/20, 8:02:30 PM",
logMessage = "Position update: 40.7128, -74.0060, altitude: 100m, battery: 85%",
),
UiMeshLog(
uuid = "4",
messageType = "Waypoint",
formattedReceivedDate = "9/27/20, 8:03:45 PM",
logMessage = "New waypoint created: 'Meeting Point' at 40.7589, -73.9851",
),
UiMeshLog(
uuid = "5",
messageType = "Error",
formattedReceivedDate = "9/27/20, 8:04:20 PM",
logMessage = "Connection timeout - retrying in 5 seconds...",
),
)
)
// Note: This preview shows the UI structure but won't have actual data
// since the ViewModel isn't injected in previews
Surface {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
stickyHeader {
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Column(
modifier = Modifier.padding(8.dp)
) {
Surface(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
Column(modifier = Modifier.padding(8.dp)) {
Text(
text = "Debug Screen Preview",
style = TextStyle(fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
modifier = Modifier.padding(bottom = 8.dp),
)
Text(
text = "Search and filter controls would appear here",
style = TextStyle(fontSize = 12.sp, color = Color.Gray)
style = TextStyle(fontSize = 12.sp, color = Color.Gray),
)
}
}
}
items(sampleLogs) { log ->
DebugItem(log = log)
}
items(sampleLogs) { log -> DebugItem(log = log) }
}
}
}
}
@Composable
fun DebugMenuActions(
viewModel: DebugViewModel = hiltViewModel(),
modifier: Modifier = Modifier,
) {
fun DebugMenuActions(viewModel: DebugViewModel = hiltViewModel(), modifier: Modifier = Modifier) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val logs by viewModel.meshLog.collectAsStateWithLifecycle()
var showDeleteLogsDialog by remember { mutableStateOf(false) }
IconButton(
onClick = {
scope.launch {
exportAllLogs(context, logs)
}
},
modifier = modifier.padding(4.dp)
) {
IconButton(onClick = { scope.launch { exportAllLogs(context, logs) } }, modifier = modifier.padding(4.dp)) {
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = stringResource(id = R.string.debug_logs_export)
contentDescription = stringResource(id = R.string.debug_logs_export),
)
}
IconButton(
onClick = { showDeleteLogsDialog = true },
modifier = modifier.padding(4.dp)
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(id = R.string.debug_clear)
)
IconButton(onClick = { showDeleteLogsDialog = true }, modifier = modifier.padding(4.dp)) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(id = R.string.debug_clear))
}
if (showDeleteLogsDialog) {
SimpleAlertDialog(
@@ -736,7 +616,7 @@ fun DebugMenuActions(
showDeleteLogsDialog = false
viewModel.deleteAllLogs()
},
onDismiss = { showDeleteLogsDialog = false }
onDismiss = { showDeleteLogsDialog = false },
)
}
}
@@ -767,28 +647,16 @@ private suspend fun exportAllLogs(context: Context, logs: List<UiMeshLog>) = wit
// Notify user of success
withContext(Dispatchers.Main) {
Toast.makeText(
context,
"Logs exported to ${logFile.absolutePath}",
Toast.LENGTH_LONG
).show()
Toast.makeText(context, "Logs exported to ${logFile.absolutePath}", Toast.LENGTH_LONG).show()
}
} catch (e: SecurityException) {
withContext(Dispatchers.Main) {
Toast.makeText(
context,
"Permission denied: Cannot write to Downloads folder",
Toast.LENGTH_LONG
).show()
Toast.makeText(context, "Permission denied: Cannot write to Downloads folder", Toast.LENGTH_LONG).show()
warn("Error:SecurityException: " + e.toString())
}
} catch (e: IOException) {
withContext(Dispatchers.Main) {
Toast.makeText(
context,
"Failed to write log file: ${e.message}",
Toast.LENGTH_LONG
).show()
Toast.makeText(context, "Failed to write log file: ${e.message}", Toast.LENGTH_LONG).show()
}
warn("Error:IOException: " + e.toString())
}
@@ -800,42 +668,31 @@ private fun DecodedPayloadBlock(
isSelected: Boolean,
colorScheme: ColorScheme,
searchText: String = "",
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
) {
val commonTextStyle = TextStyle(
fontSize = if (isSelected) 10.sp else 8.sp,
fontWeight = FontWeight.Bold,
color = colorScheme.primary
)
val commonTextStyle =
TextStyle(fontSize = if (isSelected) 10.sp else 8.sp, fontWeight = FontWeight.Bold, color = colorScheme.primary)
Column(modifier = modifier) {
Text(
text = stringResource(id = R.string.debug_decoded_payload),
style = commonTextStyle,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
)
Text(
text = "{",
style = commonTextStyle,
modifier = Modifier.padding(start = 8.dp, bottom = 2.dp)
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
)
Text(text = "{", style = commonTextStyle, modifier = Modifier.padding(start = 8.dp, bottom = 2.dp))
val annotatedPayload = rememberAnnotatedDecodedPayload(decodedPayload, searchText, colorScheme)
Text(
text = annotatedPayload,
softWrap = true,
style = TextStyle(
style =
TextStyle(
fontSize = if (isSelected) 10.sp else 8.sp,
fontFamily = FontFamily.Monospace,
color = colorScheme.onSurface.copy(alpha = 0.8f)
color = colorScheme.onSurface.copy(alpha = 0.8f),
),
modifier = Modifier.padding(start = 16.dp, bottom = 0.dp)
)
Text(
text = "}",
style = commonTextStyle,
modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)
modifier = Modifier.padding(start = 16.dp, bottom = 0.dp),
)
Text(text = "}", style = commonTextStyle, modifier = Modifier.padding(start = 8.dp, bottom = 4.dp))
}
}
@@ -843,23 +700,16 @@ private fun DecodedPayloadBlock(
private fun rememberAnnotatedDecodedPayload(
decodedPayload: String,
searchText: String,
colorScheme: ColorScheme
colorScheme: ColorScheme,
): AnnotatedString {
val highlightStyle = SpanStyle(
background = colorScheme.primary.copy(alpha = 0.3f),
color = colorScheme.onSurface
)
val highlightStyle = SpanStyle(background = colorScheme.primary.copy(alpha = 0.3f), color = colorScheme.onSurface)
return remember(decodedPayload, searchText) {
buildAnnotatedString {
append(decodedPayload)
if (searchText.isNotEmpty()) {
searchText.split(" ").forEach { term ->
Regex(Regex.escape(term), RegexOption.IGNORE_CASE).findAll(decodedPayload).forEach { match ->
addStyle(
style = highlightStyle,
start = match.range.first,
end = match.range.last + 1
)
addStyle(style = highlightStyle, start = match.range.first, end = match.range.last + 1)
}
}
}