add support for text based crash dumps

This commit is contained in:
Chloe committed 2026-03-19 15:25:59 -07:00
1 parent 69188075a3
commit 714f750f77
7 files changed
+53 -22

No files matched your search

+1 -1
View File
@@ -103,7 +103,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".preferences.GeodeLogViewActivity" android:name=".preferences.TextViewActivity"
android:exported="false" android:exported="false"
android:theme="@style/Theme.GeodeLauncher"> android:theme="@style/Theme.GeodeLauncher">
</activity> </activity>
@@ -13,7 +13,8 @@ import kotlin.time.Instant
data class CrashDump( data class CrashDump(
val filename: String, val filename: String,
val lastModified: Instant val lastModified: Instant,
val fullPath: String,
) )
class CrashViewModel(crashDirectory: File) : BaseDirectoryViewModel<CrashDump>(crashDirectory) { class CrashViewModel(crashDirectory: File) : BaseDirectoryViewModel<CrashDump>(crashDirectory) {
@@ -41,16 +42,19 @@ class CrashViewModel(crashDirectory: File) : BaseDirectoryViewModel<CrashDump>(c
val children = crashDirectory.listFiles { val children = crashDirectory.listFiles {
// ignore indicator files (including old file) // ignore indicator files (including old file)
_, name -> name != LaunchUtils.CRASH_INDICATOR_NAME && name != "last-pid" _, name -> !LaunchUtils.CRASH_INDICATOR_NAMES.contains(name)
} ?: return emptyList() } ?: return emptyList()
hasIndicator = File(crashDirectory, LaunchUtils.CRASH_INDICATOR_NAME).exists() hasIndicator = LaunchUtils.CRASH_INDICATOR_NAMES.any {
File(crashDirectory, it).exists()
}
return children return children
.map { .map {
CrashDump( CrashDump(
filename = it.name, filename = it.name,
lastModified = Instant.fromEpochMilliseconds(it.lastModified()) lastModified = Instant.fromEpochMilliseconds(it.lastModified()),
fullPath = it.path,
) )
} }
.sortedByDescending { it.lastModified } .sortedByDescending { it.lastModified }
@@ -58,7 +62,12 @@ class CrashViewModel(crashDirectory: File) : BaseDirectoryViewModel<CrashDump>(c
fun clearIndicator() { fun clearIndicator() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
File(baseDirectory, LaunchUtils.CRASH_INDICATOR_NAME).delete() LaunchUtils.CRASH_INDICATOR_NAMES.forEach {
val crashFile = File(baseDirectory, it)
if (crashFile.exists()) {
crashFile.delete()
}
}
hasIndicator = false hasIndicator = false
} }
@@ -66,7 +75,8 @@ class CrashViewModel(crashDirectory: File) : BaseDirectoryViewModel<CrashDump>(c
fun createIndicator() { fun createIndicator() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
File(baseDirectory, LaunchUtils.CRASH_INDICATOR_NAME).createNewFile() val crashFile = File(baseDirectory, LaunchUtils.CRASH_INDICATOR_NAMES.first())
crashFile.createNewFile()
hasIndicator = true hasIndicator = true
} }
@@ -14,6 +14,7 @@ data class GeodeLog(
val filename: String, val filename: String,
val lastModified: Instant, val lastModified: Instant,
val fileSize: Long, val fileSize: Long,
val fullPath: String,
) )
class GeodeLogsViewModel(crashDirectory: File) : BaseDirectoryViewModel<GeodeLog>(crashDirectory) { class GeodeLogsViewModel(crashDirectory: File) : BaseDirectoryViewModel<GeodeLog>(crashDirectory) {
@@ -43,7 +44,8 @@ class GeodeLogsViewModel(crashDirectory: File) : BaseDirectoryViewModel<GeodeLog
GeodeLog( GeodeLog(
filename = it.name, filename = it.name,
lastModified = Instant.fromEpochMilliseconds(it.lastModified()), lastModified = Instant.fromEpochMilliseconds(it.lastModified()),
fileSize = it.length() fileSize = it.length(),
fullPath = it.path,
) )
} }
.sortedByDescending { it.lastModified } .sortedByDescending { it.lastModified }
@@ -12,6 +12,7 @@ import androidx.activity.OnBackPressedDispatcher
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -110,11 +111,20 @@ fun shareCrash(context: Context, filename: String) {
@Composable @Composable
fun CrashCard(crashDump: CrashDump, crashViewModel: CrashViewModel, modifier: Modifier = Modifier) { fun CrashCard(crashDump: CrashDump, crashViewModel: CrashViewModel, modifier: Modifier = Modifier) {
val isReadable = crashDump.filename.endsWith(".txt")
val context = LocalContext.current
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 1.dp, horizontal = 8.dp) .padding(vertical = 1.dp, horizontal = 8.dp)
.height(64.dp) .height(64.dp)
.then(modifier) .then(modifier)
.clickable(isReadable) {
val launchIntent = Intent(context, TextViewActivity::class.java).run {
putExtra(TextViewActivity.EXTRA_LOG_VIEW_FILENAME, crashDump.fullPath)
}
context.startActivity(launchIntent)
}
.background(MaterialTheme.colorScheme.surfaceContainerLow) .background(MaterialTheme.colorScheme.surfaceContainerLow)
.padding(vertical = 8.dp, horizontal = 8.dp) .padding(vertical = 8.dp, horizontal = 8.dp)
) { ) {
@@ -117,8 +117,8 @@ fun GeodeLogCard(geodeLog: GeodeLog, geodeLogsViewModel: GeodeLogsViewModel, mod
.height(64.dp) .height(64.dp)
.then(modifier) .then(modifier)
.clickable { .clickable {
val launchIntent = Intent(context, GeodeLogViewActivity::class.java).run { val launchIntent = Intent(context, TextViewActivity::class.java).run {
putExtra(GeodeLogViewActivity.EXTRA_LOG_VIEW_FILENAME, geodeLog.filename) putExtra(TextViewActivity.EXTRA_LOG_VIEW_FILENAME, geodeLog.fullPath)
} }
context.startActivity(launchIntent) context.startActivity(launchIntent)
} }
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
@@ -40,18 +39,19 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.geode.launcher.R import com.geode.launcher.R
import com.geode.launcher.log.GeodeLogsViewModel
import com.geode.launcher.ui.theme.GeodeLauncherTheme import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.LocalTheme import com.geode.launcher.ui.theme.LocalTheme
import com.geode.launcher.ui.theme.Theme import com.geode.launcher.ui.theme.Theme
import com.geode.launcher.ui.theme.Typography import com.geode.launcher.ui.theme.Typography
import com.geode.launcher.ui.theme.robotoMonoFamily import com.geode.launcher.ui.theme.robotoMonoFamily
import com.geode.launcher.utils.PreferenceUtils import com.geode.launcher.utils.PreferenceUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
class GeodeLogViewActivity : ComponentActivity() { class TextViewActivity : ComponentActivity() {
companion object { companion object {
const val EXTRA_LOG_VIEW_FILENAME = "EXTRA_LOG_FILENAME" const val EXTRA_LOG_VIEW_FILENAME = "EXTRA_LOG_FILENAME"
} }
@@ -95,14 +95,23 @@ class GeodeLogViewActivity : ComponentActivity() {
fun GeodeLogView( fun GeodeLogView(
onBackPressedDispatcher: OnBackPressedDispatcher?, onBackPressedDispatcher: OnBackPressedDispatcher?,
filename: String, filename: String,
geodeLogsViewModel: GeodeLogsViewModel = viewModel(factory = GeodeLogsViewModel.Factory)
) { ) {
var loadingFile by remember { mutableStateOf(true) } var loadingFile by remember { mutableStateOf(true) }
var description by remember { mutableStateOf(emptyList<String>()) } var description by remember { mutableStateOf(emptyList<String>()) }
val toRead = File(filename)
LaunchedEffect(filename) { LaunchedEffect(filename) {
loadingFile = true loadingFile = true
description = geodeLogsViewModel.getFileText(filename)
withContext(Dispatchers.IO) {
if (!toRead.isFile) {
description = listOf("File does not exist!")
} else {
description = toRead.readLines()
}
}
loadingFile = false loadingFile = false
} }
@@ -123,7 +132,7 @@ fun GeodeLogView(
}, },
title = { title = {
Text( Text(
filename, toRead.name,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -73,7 +73,7 @@ object LaunchUtils {
return File(getBaseDirectory(context), "save") return File(getBaseDirectory(context), "save")
} }
const val CRASH_INDICATOR_NAME = "lastSessionDidCrash" val CRASH_INDICATOR_NAMES = setOf("lastSessionDidCrash", "last-pid", "last-crashed")
fun getCrashDirectory(context: Context): File { fun getCrashDirectory(context: Context): File {
val base = getBaseDirectory(context) val base = getBaseDirectory(context)
@@ -93,7 +93,7 @@ object LaunchUtils {
val children = crashDirectory.listFiles { val children = crashDirectory.listFiles {
// ignore indicator files (including old file) // ignore indicator files (including old file)
_, name -> name != CRASH_INDICATOR_NAME && name != "last-pid" _, name -> !CRASH_INDICATOR_NAMES.contains(name)
} }
return children?.maxByOrNull { it.lastModified() } return children?.maxByOrNull { it.lastModified() }
@@ -101,9 +101,9 @@ object LaunchUtils {
fun lastSessionCrashed(context: Context): Boolean { fun lastSessionCrashed(context: Context): Boolean {
val base = getCrashDirectory(context) val base = getCrashDirectory(context)
val crashIndicatorFile = File(base, CRASH_INDICATOR_NAME) return CRASH_INDICATOR_NAMES.any {
File(base, it).exists()
return crashIndicatorFile.exists() }
} }
enum class LauncherError : Serializable { enum class LauncherError : Serializable {