1package soy.iko.opencode.ui.settings
3import android.content.Intent
4import androidx.compose.foundation.clickable
5import androidx.compose.foundation.layout.Arrangement
6import androidx.compose.foundation.layout.Column
7import androidx.compose.foundation.layout.Row
8import androidx.compose.foundation.layout.Spacer
9import androidx.compose.foundation.layout.fillMaxSize
10import androidx.compose.foundation.layout.fillMaxWidth
11import androidx.compose.foundation.layout.padding
12import androidx.compose.foundation.layout.size
13import androidx.compose.foundation.lazy.LazyColumn
14import androidx.compose.foundation.lazy.items
15import androidx.compose.material.icons.Icons
16import androidx.compose.material.icons.automirrored.filled.ArrowBack
17import androidx.compose.material.icons.filled.BugReport
18import androidx.compose.material.icons.filled.DeleteSweep
19import androidx.compose.material.icons.filled.Share
20import androidx.compose.material3.AlertDialog
21import androidx.compose.material3.ExperimentalMaterial3Api
22import androidx.compose.material3.HorizontalDivider
23import androidx.compose.material3.Icon
24import androidx.compose.material3.IconButton
25import androidx.compose.material3.MaterialTheme
26import androidx.compose.material3.Scaffold
27import androidx.compose.material3.Text
28import androidx.compose.material3.TextButton
29import androidx.compose.material3.TopAppBar
30import androidx.compose.runtime.Composable
31import androidx.compose.runtime.LaunchedEffect
32import androidx.compose.runtime.getValue
33import androidx.compose.runtime.mutableStateOf
34import androidx.compose.runtime.remember
35import androidx.compose.runtime.rememberCoroutineScope
36import androidx.compose.runtime.setValue
37import kotlinx.coroutines.Dispatchers
38import kotlinx.coroutines.launch
39import kotlinx.coroutines.withContext
40import androidx.compose.ui.Alignment
41import androidx.compose.ui.Modifier
42import androidx.compose.ui.platform.LocalContext
43import androidx.compose.ui.res.stringResource
44import androidx.compose.ui.semantics.Role
45import androidx.compose.ui.semantics.heading
46import androidx.compose.ui.semantics.role
47import androidx.compose.ui.semantics.semantics
48import androidx.compose.ui.unit.dp
49import androidx.lifecycle.compose.collectAsStateWithLifecycle
50import soy.iko.opencode.R
51import soy.iko.opencode.data.repo.CrashLogger
52import soy.iko.opencode.ui.components.rememberRelativeTime
54@OptIn(ExperimentalMaterial3Api::class)
56fun DiagnosticsScreen(onBack: () -> Unit) {
57 val context = LocalContext.current
58 val logger = remember { CrashLogger.get(context) }
59 val reports by logger.reports.collectAsStateWithLifecycle()
60 var viewing by remember { mutableStateOf<String?>(null) }
61 val shareScope = rememberCoroutineScope()
62 val shareLabel = stringResource(R.string.share)
67 title = { Text(stringResource(R.string.diagnostics)) },
69 IconButton(onClick = onBack) {
70 Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back))
74 if (reports.isNotEmpty()) {
75 IconButton(onClick = {
76 shareScope.launch(Dispatchers.IO) { logger.clearAll() }
78 Icon(Icons.Filled.DeleteSweep, contentDescription = stringResource(R.string.clear_all))
85 Column(modifier = Modifier.fillMaxSize().padding(padding)) {
87 stringResource(R.string.diagnostics_desc),
88 style = MaterialTheme.typography.bodySmall,
89 color = MaterialTheme.colorScheme.onSurfaceVariant,
90 modifier = Modifier.padding(16.dp),
93 stringResource(R.string.crash_reports),
94 style = MaterialTheme.typography.titleSmall,
95 color = MaterialTheme.colorScheme.primary,
97 .padding(start = 16.dp, bottom = 8.dp)
98 .semantics { heading() },
100 if (reports.isEmpty()) {
102 stringResource(R.string.no_crash_reports),
103 style = MaterialTheme.typography.bodyMedium,
104 color = MaterialTheme.colorScheme.onSurfaceVariant,
105 modifier = Modifier.padding(24.dp),
108 LazyColumn(modifier = Modifier.fillMaxSize()) {
109 items(reports, key = { it.fileName }) { report ->
113 .clickable(role = Role.Button) { viewing = report.fileName }
114 .padding(horizontal = 16.dp, vertical = 12.dp),
115 verticalAlignment = Alignment.CenterVertically,
116 horizontalArrangement = Arrangement.spacedBy(12.dp),
119 Icons.Filled.BugReport,
120 contentDescription = null,
121 tint = MaterialTheme.colorScheme.error,
123 Column(modifier = Modifier.weight(1f)) {
124 Text(report.preview, style = MaterialTheme.typography.bodyMedium, maxLines = 1)
126 rememberRelativeTime(report.timestamp),
127 style = MaterialTheme.typography.bodySmall,
128 color = MaterialTheme.colorScheme.onSurfaceVariant,
131 IconButton(onClick = {
133 val content = withContext(Dispatchers.IO) {
134 logger.readReport(report.fileName).orEmpty()
136 val send = Intent(Intent.ACTION_SEND).apply {
138 putExtra(Intent.EXTRA_SUBJECT, shareLabel)
139 putExtra(Intent.EXTRA_TEXT, content)
141 runCatching { context.startActivity(Intent.createChooser(send, shareLabel)) }
144 Icon(Icons.Filled.Share, contentDescription = stringResource(R.string.share))
154 val reportName = viewing
155 if (reportName != null) {
156 var reportContent by remember(reportName) { mutableStateOf<String?>(null) }
157 LaunchedEffect(reportName) {
158 reportContent = withContext(Dispatchers.IO) { logger.readReport(reportName) }
160 val content = reportContent
162 onDismissRequest = { viewing = null },
164 TextButton(onClick = { viewing = null }) { Text(stringResource(R.string.back)) }
168 val shareLabel2 = stringResource(R.string.share)
169 TextButton(onClick = {
170 val send = Intent(Intent.ACTION_SEND).apply {
172 putExtra(Intent.EXTRA_SUBJECT, shareLabel2)
173 putExtra(Intent.EXTRA_TEXT, content.orEmpty())
175 runCatching { context.startActivity(Intent.createChooser(send, shareLabel2)) }
176 }) { Text(stringResource(R.string.share)) }
177 Spacer(Modifier.size(8.dp))
178 TextButton(onClick = {
179 shareScope.launch(Dispatchers.IO) { logger.deleteReport(reportName) }
181 }) { Text(stringResource(R.string.delete)) }
184 title = { Text(stringResource(R.string.crash_report)) },
188 style = MaterialTheme.typography.bodySmall,
189 modifier = Modifier.fillMaxWidth(),
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list