Skip to content

Commit

Permalink
[feature] Support showing crash information
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyD666 committed Apr 26, 2024
1 parent a507e99 commit d53feaa
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 1 deletion.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 65
versionName = "2.2-rc03"
versionName = "2.2-rc04"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
</intent-filter>
</activity>

<activity android:name=".ui.activity.CrashActivity" />

<service
android:name=".api.ApiService"
android:enabled="true"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/skyd/rays/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.appcompat.app.AppCompatDelegate
import com.skyd.rays.ext.dataStore
import com.skyd.rays.ext.getOrDefault
import com.skyd.rays.model.preference.theme.DarkModePreference
import com.skyd.rays.util.CrashHandler
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
Expand All @@ -16,6 +17,8 @@ class App : Application() {
appContext = this

AppCompatDelegate.setDefaultNightMode(dataStore.getOrDefault(DarkModePreference))

CrashHandler.init(this)
}
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/skyd/rays/config/Config.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.skyd.rays.config

const val GITHUB_REPO = "https://github.com/SkyD666/Rays-Android"
const val GITHUB_NEW_ISSUE_URL = "https://github.com/SkyD666/Rays-Android/issues/new"

const val WEBLATE_URL = "https://hosted.weblate.org/engage/rays/"

Expand Down
173 changes: 173 additions & 0 deletions app/src/main/java/com/skyd/rays/ui/activity/CrashActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.skyd.rays.ui.activity

import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.filled.BugReport
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import com.skyd.rays.R
import com.skyd.rays.config.GITHUB_NEW_ISSUE_URL
import com.skyd.rays.ext.showSnackbar
import com.skyd.rays.model.preference.SettingsProvider
import com.skyd.rays.ui.local.LocalDarkMode
import com.skyd.rays.ui.local.LocalWindowSizeClass
import com.skyd.rays.ui.theme.RaysTheme
import com.skyd.rays.util.CommonUtil.getAppVersionCode
import com.skyd.rays.util.CommonUtil.getAppVersionName
import com.skyd.rays.util.CommonUtil.openBrowser

/**
* CrashActivity
*/
class CrashActivity : ComponentActivity() {
companion object {
const val CRASH_INFO = "crashInfo"

fun start(context: Context, crashInfo: String) {
val intent = Intent(context, CrashActivity::class.java)
intent.putExtra(CRASH_INFO, crashInfo)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)

val crashInfo = intent.getStringExtra(CRASH_INFO)
val message = buildString {
append("VersionName: ").append(getAppVersionName()).append("\n")
append("VersionCode: ").append(getAppVersionCode()).append("\n")
append("Brand: ").append(Build.BRAND).append("\n")
append("Model: ").append(Build.MODEL).append("\n")
append("SDK Version: ").append(Build.VERSION.SDK_INT).append("\n")
append("ABI: ").append(Build.SUPPORTED_ABIS.firstOrNull().orEmpty()).append("\n\n")
append("Crash Info: \n")
append(crashInfo)
}

setContent {
CompositionLocalProvider(
LocalWindowSizeClass provides calculateWindowSizeClass(this)
) {
SettingsProvider {
RaysTheme(darkTheme = LocalDarkMode.current) {
CrashScreen(
message = message,
onReport = { openBrowser(GITHUB_NEW_ISSUE_URL) }
)
}
}
}
}
}
}

@Composable
private fun CrashScreen(
message: String,
onReport: () -> Unit,
) {
val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
val scope = rememberCoroutineScope()
val clipboardManager = LocalClipboardManager.current

Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(it)
.padding(20.dp)
) {
Spacer(modifier = Modifier.height(40.dp))
Icon(
modifier = Modifier.size(40.dp),
imageVector = Icons.Default.BugReport,
contentDescription = null,
)

Spacer(modifier = Modifier.height(30.dp))
Text(
text = stringResource(id = R.string.crashed),
style = MaterialTheme.typography.headlineLarge,
)

Spacer(modifier = Modifier.height(30.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
TextButton(onClick = {
copyToClipboard(message, clipboardManager)
snackbarHostState.showSnackbar(
scope = scope,
message = context.getString(R.string.copied),
)
}) {
Text(text = stringResource(id = R.string.crash_screen_copy_crash_log))
}

Spacer(modifier = Modifier.width(12.dp))

Button(onClick = {
copyToClipboard(message, clipboardManager)
onReport()
}) {
Text(text = stringResource(id = R.string.submit_an_issue_on_github))
}
}

Spacer(modifier = Modifier.height(20.dp))
Text(
text = stringResource(R.string.crash_screen_crash_log),
style = MaterialTheme.typography.titleLarge,
)

Spacer(modifier = Modifier.height(10.dp))
SelectionContainer {
Text(text = message, style = MaterialTheme.typography.bodyMedium)
}
}
}
}

private fun copyToClipboard(text: String, clipboardManager: ClipboardManager) {
clipboardManager.setText(AnnotatedString(text))
}
64 changes: 64 additions & 0 deletions app/src/main/java/com/skyd/rays/util/CrashHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.skyd.rays.util

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import com.skyd.rays.ui.activity.CrashActivity
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.system.exitProcess


class CrashHandler private constructor(
val context: Context
) : Thread.UncaughtExceptionHandler {
private val mDefaultHandler: Thread.UncaughtExceptionHandler? =
Thread.getDefaultUncaughtExceptionHandler()

/**
* When UncaughtException occurs, this function will handle it.
*/
override fun uncaughtException(thread: Thread, ex: Throwable) {
try {
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
ex.printStackTrace(printWriter)
var cause = ex.cause
while (cause != null) {
cause.printStackTrace(printWriter)
cause = cause.cause
}
printWriter.close()
val unCaughtException = stringWriter.toString()
Log.e("Crash Info", unCaughtException)
CrashActivity.start(context, unCaughtException)
exitProcess(0)
} catch (e: Exception) {
e.printStackTrace()
}
mDefaultHandler?.uncaughtException(thread, ex)
}

companion object {
@SuppressLint("StaticFieldLeak")
private var instance: CrashHandler? = null

fun init(context: Context): CrashHandler? {
if (instance == null) {
synchronized(CrashHandler::class.java) {
if (instance == null) {
instance = CrashHandler(context)
}
}
}
return instance
}
}

/**
* Only one CrashHandler can be initialized
*/
init {
Thread.setDefaultUncaughtExceptionHandler(this)
}
}
5 changes: 5 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,9 @@
<string name="cache_screen_delete_all_mimetypes">删除 Mimetype</string>
<string name="cache_screen_delete_all_mimetypes_description">删除所有已缓存的表情包 Mimetype 数据</string>
<string name="cache_screen_delete_all_mimetypes_success">已删除 Mimetype 数据,花费 %.2f 秒</string>
<string name="crashed"><![CDATA[崩溃了 (#><)]]></string>
<string name="submit_an_issue_on_github">在 GitHub 上报告</string>
<string name="copied">已复制</string>
<string name="crash_screen_crash_log">日志</string>
<string name="crash_screen_copy_crash_log">复制日志</string>
</resources>
5 changes: 5 additions & 0 deletions app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,9 @@
<string name="cache_screen_delete_all_mimetypes">刪除 Mimetype</string>
<string name="cache_screen_delete_all_mimetypes_description">刪除快取的 Mimetype 資料</string>
<string name="cache_screen_delete_all_mimetypes_success">所有 Mimetype 已刪除,花費了 %.2f 秒</string>
<string name="crashed"><![CDATA[崩潰了 (#><)]]></string>
<string name="submit_an_issue_on_github">在 GitHub 上報告</string>
<string name="copied">已複製</string>
<string name="crash_screen_crash_log">日誌</string>
<string name="crash_screen_copy_crash_log">複製日誌</string>
</resources>
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,9 @@
<string name="cache_screen_delete_all_mimetypes">Delete mimetypes</string>
<string name="cache_screen_delete_all_mimetypes_description">Delete cached Mimetype data</string>
<string name="cache_screen_delete_all_mimetypes_success">All Mimetype deleted, spent %.2fs</string>
<string name="crashed"><![CDATA[Crashed (#><)]]></string>
<string name="submit_an_issue_on_github">Report on GitHub</string>
<string name="copied">Copied</string>
<string name="crash_screen_crash_log">Log</string>
<string name="crash_screen_copy_crash_log">Copy log</string>
</resources>

0 comments on commit d53feaa

Please sign in to comment.