diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 92ae6dd..b0ee2ae 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,6 +24,9 @@
+
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/TodoApp.kt b/app/src/main/kotlin/cn/super12138/todo/TodoApp.kt
index 959823a..c13057f 100644
--- a/app/src/main/kotlin/cn/super12138/todo/TodoApp.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/TodoApp.kt
@@ -2,6 +2,7 @@ package cn.super12138.todo
import android.app.Application
import cn.super12138.todo.logic.database.TodoDatabase
+import cn.super12138.todo.ui.pages.crash.CrashHandler
class TodoApp : Application() {
private val database by lazy { TodoDatabase.getDatabase(this) }
@@ -12,6 +13,10 @@ class TodoApp : Application() {
override fun onCreate() {
super.onCreate()
+
db = database
+
+ val crashHandler = CrashHandler(applicationContext)
+ Thread.setDefaultUncaughtExceptionHandler(crashHandler)
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt
new file mode 100644
index 0000000..dc439fc
--- /dev/null
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt
@@ -0,0 +1,66 @@
+package cn.super12138.todo.ui.activities
+
+import android.os.Build
+import android.os.Bundle
+import android.view.HapticFeedbackConstants
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.stringResource
+import cn.super12138.todo.R
+import cn.super12138.todo.ui.pages.crash.CrashPage
+import cn.super12138.todo.ui.theme.ToDoTheme
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Locale
+
+class CrashActivity : ComponentActivity() {
+ companion object {
+ const val BRAND_PREFIX = "Brand: "
+ const val MODEL_PREFIX = "Model: "
+ const val DEVICE_SDK_PREFIX = "Device SDK: "
+ const val CRASH_TIME_PREFIX = "Crash time: "
+ const val BEGINNING_CRASH = "======beginning of crash======"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+
+ val crashLogs = intent.getStringExtra("crash_logs")
+
+ val deviceBrand = Build.BRAND
+ val deviceModel = Build.MODEL
+ val sdkLevel = Build.VERSION.SDK_INT
+ val currentDateTime = Calendar.getInstance().time
+ val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
+ val formattedDateTime = formatter.format(currentDateTime)
+
+ val deviceInfo = StringBuilder().apply {
+ append(BRAND_PREFIX).append("").append(deviceBrand).append('\n')
+ append(MODEL_PREFIX).append(deviceModel).append('\n')
+ append(DEVICE_SDK_PREFIX).append(sdkLevel).append('\n').append('\n')
+ append(CRASH_TIME_PREFIX).append(formattedDateTime).append('\n').append('\n')
+ append(BEGINNING_CRASH).append('\n')
+ }
+
+ val crashLogFormatted = StringBuilder().apply {
+ append(deviceInfo)
+ append(crashLogs)
+ }.toString()
+
+ setContent {
+ ToDoTheme {
+ val view = LocalView.current
+ CrashPage(
+ crashLog = if (crashLogs == null) stringResource(R.string.tip_no_crash_logs) else crashLogFormatted,
+ exitApp = {
+ view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
+ finishAffinity()
+ }
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashHandler.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashHandler.kt
new file mode 100644
index 0000000..028d28f
--- /dev/null
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashHandler.kt
@@ -0,0 +1,28 @@
+package cn.super12138.todo.ui.pages.crash
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import cn.super12138.todo.ui.activities.CrashActivity
+import kotlin.system.exitProcess
+
+class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
+ private val defaultUEH = Thread.getDefaultUncaughtExceptionHandler()
+
+ override fun uncaughtException(thread: Thread, ex: Throwable) {
+ val stackTrace = ex.stackTraceToString()
+
+ // 启动新的 Activity 来显示崩溃日志
+ val intent = Intent(context, CrashActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ putExtra("crash_logs", stackTrace)
+ }
+ context.startActivity(intent)
+
+ // 传递异常给默认的异常处理器
+ defaultUEH?.uncaughtException(thread, ex)
+
+ // 杀掉崩溃的应用程序进程
+ Process.killProcess(Process.myPid())
+ exitProcess(10)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt
new file mode 100644
index 0000000..d0397bf
--- /dev/null
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt
@@ -0,0 +1,91 @@
+package cn.super12138.todo.ui.pages.crash
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeContent
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.selection.SelectionContainer
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ExitToApp
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExtendedFloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import cn.super12138.todo.R
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CrashPage(
+ crashLog: String,
+ exitApp: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+ Scaffold(
+ modifier = modifier
+ .fillMaxSize()
+ .nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ LargeTopAppBar(
+ title = {
+ Text(
+ text = stringResource(R.string.page_crash),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ scrollBehavior = scrollBehavior
+ )
+ },
+ floatingActionButton = {
+ ExtendedFloatingActionButton(
+ onClick = exitApp,
+ icon = {
+ Icon(
+ imageVector = Icons.AutoMirrored.Outlined.ExitToApp,
+ contentDescription = stringResource(R.string.action_exit_app)
+ )
+ },
+ text = {
+ Text(
+ text = stringResource(R.string.action_exit_app)
+ )
+ },
+ )
+ },
+ contentWindowInsets = WindowInsets.safeContent
+ ) { innerPadding ->
+ Column(
+ modifier = Modifier
+ .padding(innerPadding)
+ .padding(horizontal = 16.dp)
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState())
+ ) {
+ Spacer(Modifier.height(5.dp))
+ SelectionContainer {
+ Text(
+ text = crashLog,
+ fontFamily = FontFamily.Monospace,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index c28d6a8..c1b4bd2 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -22,4 +22,7 @@
其它
修改待办
剩余 %s 项任务
+ 应用程序出现错误
+ 没有日志传入
+ 退出应用
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9dbf90f..ec41fd3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,4 +21,7 @@
Others
Edit Task
%s tasks remaining
+ Oops! App went wrong
+ No crash logs
+ Exit app
\ No newline at end of file