Log screen: add scroll to end button

This commit is contained in:
MM20 2023-10-02 11:13:13 +02:00
parent 633cc0214d
commit 9d909e718c
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
2 changed files with 52 additions and 11 deletions

View File

@ -12,6 +12,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.HelpOutline import androidx.compose.material.icons.rounded.HelpOutline
@ -48,6 +50,7 @@ fun PreferenceScreen(
floatingActionButton: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {},
topBarActions: @Composable RowScope.() -> Unit = {}, topBarActions: @Composable RowScope.() -> Unit = {},
helpUrl: String? = null, helpUrl: String? = null,
lazyColumnState: LazyListState = rememberLazyListState(),
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
PreferenceScreen( PreferenceScreen(
@ -62,6 +65,7 @@ fun PreferenceScreen(
floatingActionButton = floatingActionButton, floatingActionButton = floatingActionButton,
topBarActions = topBarActions, topBarActions = topBarActions,
helpUrl = helpUrl, helpUrl = helpUrl,
lazyColumnState = lazyColumnState,
content = content content = content
) )
} }
@ -72,6 +76,7 @@ fun PreferenceScreen(
floatingActionButton: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {},
topBarActions: @Composable RowScope.() -> Unit = {}, topBarActions: @Composable RowScope.() -> Unit = {},
helpUrl: String? = null, helpUrl: String? = null,
lazyColumnState: LazyListState = rememberLazyListState(),
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
val navController = LocalNavController.current val navController = LocalNavController.current
@ -149,6 +154,7 @@ fun PreferenceScreen(
.fillMaxSize() .fillMaxSize()
.nestedScroll(nestedScrollConnection) .nestedScroll(nestedScrollConnection)
.padding(it), .padding(it),
state = lazyColumnState,
content = content, content = content,
) )
} }

View File

@ -1,13 +1,18 @@
package de.mm20.launcher2.ui.settings.log package de.mm20.launcher2.ui.settings.log
import android.content.Intent import android.content.Intent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowDownward
import androidx.compose.material.icons.rounded.BugReport import androidx.compose.material.icons.rounded.BugReport
import androidx.compose.material.icons.rounded.Error import androidx.compose.material.icons.rounded.Error
import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Info
@ -16,6 +21,8 @@ import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -45,12 +52,15 @@ import java.util.regex.Pattern
@Composable @Composable
fun LogScreen() { fun LogScreen() {
var lines by remember { mutableStateOf(emptyList<LogcatLine>()) } var lines by remember { mutableStateOf(emptyList<LogcatLine>()) }
val listState = rememberLazyListState()
LaunchedEffect(null) { LaunchedEffect(null) {
val process = Runtime.getRuntime().exec("/system/bin/logcat -v time") val process = Runtime.getRuntime().exec("/system/bin/logcat -v time")
val pattern = Pattern.compile( val pattern = Pattern.compile(
"^(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3})\\s+" + /* timestamp [1] */ "^(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3})\\s+" + /* timestamp [1] */
"(\\w)/(.+?)\\(\\s*(\\d+)\\): (.*)$") /* level, tag, pid, msg [2-5] */ "(\\w)/(.+?)\\(\\s*(\\d+)\\): (.*)$"
) /* level, tag, pid, msg [2-5] */
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
val inputStream = process.inputStream.bufferedReader() val inputStream = process.inputStream.bufferedReader()
@ -108,31 +118,56 @@ fun LogScreen() {
}) { }) {
Icon(Icons.Rounded.Share, contentDescription = null) Icon(Icons.Rounded.Share, contentDescription = null)
} }
} },
floatingActionButton = {
AnimatedVisibility(
listState.canScrollForward,
enter = scaleIn(),
exit = scaleOut(),
) {
SmallFloatingActionButton(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
onClick = {
scope.launch {
listState.animateScrollToItem(lines.lastIndex)
}
}) {
Icon(Icons.Rounded.ArrowDownward, null)
}
}
},
lazyColumnState = listState,
) { ) {
items(lines) { items(lines) {
if (it is RawLogcatLine) { if (it is RawLogcatLine) {
Text(modifier = Modifier.padding(16.dp), text = it.line ?: "", style = MaterialTheme.typography.bodySmall) Text(
modifier = Modifier.padding(16.dp),
text = it.line ?: "",
style = MaterialTheme.typography.bodySmall
)
} else if (it is FormattedLogcatLine) { } else if (it is FormattedLogcatLine) {
val contentColor = when(it.level) { val contentColor = when (it.level) {
"E" -> MaterialTheme.colorScheme.onErrorContainer "E" -> MaterialTheme.colorScheme.onErrorContainer
"W" -> MaterialTheme.colorScheme.onPrimaryContainer "W" -> MaterialTheme.colorScheme.onPrimaryContainer
"D" -> MaterialTheme.colorScheme.onSurfaceVariant "D" -> MaterialTheme.colorScheme.onSurfaceVariant
else -> MaterialTheme.colorScheme.onSurface else -> MaterialTheme.colorScheme.onSurface
} }
val bgColor = when(it.level) { val bgColor = when (it.level) {
"E" -> MaterialTheme.colorScheme.errorContainer "E" -> MaterialTheme.colorScheme.errorContainer
"W" -> MaterialTheme.colorScheme.primaryContainer "W" -> MaterialTheme.colorScheme.primaryContainer
"D" -> MaterialTheme.colorScheme.surfaceVariant "D" -> MaterialTheme.colorScheme.surfaceVariant
else -> MaterialTheme.colorScheme.surface else -> MaterialTheme.colorScheme.surface
} }
Column(modifier = Modifier Column(
.fillMaxWidth() modifier = Modifier
.background(bgColor) .fillMaxWidth()
.padding(16.dp)) { .background(bgColor)
.padding(16.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Icon( Icon(
when(it.level) { when (it.level) {
"E" -> Icons.Rounded.Error "E" -> Icons.Rounded.Error
"W" -> Icons.Rounded.Warning "W" -> Icons.Rounded.Warning
"D" -> Icons.Rounded.BugReport "D" -> Icons.Rounded.BugReport
@ -169,4 +204,4 @@ data class FormattedLogcatLine(
val message: String, val message: String,
) : LogcatLine ) : LogcatLine
data class RawLogcatLine(val line: String): LogcatLine data class RawLogcatLine(val line: String) : LogcatLine