Skip to content

Commit

Permalink
[feature] Add unknown exception display page
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyD666 committed Mar 5, 2024
1 parent 291e948 commit 78e25fb
Show file tree
Hide file tree
Showing 8 changed files with 169 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 @@ -20,7 +20,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 8
versionName = "1.0-rc02"
versionName = "1.0-rc03"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

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 @@ -64,6 +64,8 @@
</intent-filter>
</activity>

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

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/skyd/anivu/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application
import android.content.Context
import com.google.android.material.color.DynamicColors
import com.skyd.anivu.model.worker.rsssync.listenerRssSyncFrequency
import com.skyd.anivu.util.CrashHandler
import dagger.hilt.android.HiltAndroidApp


Expand All @@ -13,6 +14,9 @@ class App : Application() {
override fun onCreate() {
super.onCreate()
appContext = this

CrashHandler.init(this)

DynamicColors.applyToActivitiesIfAvailable(this)

listenerRssSyncFrequency(this)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/skyd/anivu/config/Const.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.io.File

object Const {
const val GITHUB_REPO = "https://github.com/SkyD666/AniVu"
const val GITHUB_NEW_ISSUE_URL = "https://github.com/SkyD666/AniVu/issues/new"
const val TELEGRAM_GROUP = "https://t.me/SkyD666Chat"
const val DISCORD_SERVER = "https://discord.gg/pEWEjeJTa3"

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

import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Process
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.skyd.anivu.R
import com.skyd.anivu.config.Const.GITHUB_NEW_ISSUE_URL
import com.skyd.anivu.ext.getAppVersionName
import com.skyd.anivu.ext.openBrowser
import com.skyd.anivu.ext.sp
import com.skyd.anivu.ui.component.showToast
import kotlin.system.exitProcess


/**
* 调试用的异常activity,不要继承BaseActivity
*/
class CrashActivity : AppCompatActivity() {
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)
}
}

private fun copyToClipboard(crashInfo: String?) {
val cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cm.setPrimaryClip(ClipData.newPlainText("Exception trace stack", crashInfo))
}

private fun exitApp() {
finish()
Process.killProcess(Process.myPid())
exitProcess(1)
}

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("Brand: ").append(Build.BRAND).append("\n")
append("Model: ").append(Build.MODEL).append("\n")
append("SDK Version: ").append(Build.VERSION.SDK_INT).append("\n\n")
append("Crash Info: \n")
append(crashInfo)
}
MaterialAlertDialogBuilder(this)
.setIcon(R.drawable.ic_error_24)
.setTitle(getString(R.string.crashed))
.setMessage(message)
.setCancelable(false)
.setPositiveButton(getString(R.string.submit_an_issue_on_github)) { _, _ ->
copyToClipboard(crashInfo)
Uri.parse(GITHUB_NEW_ISSUE_URL).openBrowser(this)
exitApp()
}
.setNegativeButton(getString(R.string.close)) { _, _ ->
exitApp()
}
.setNeutralButton(getString(android.R.string.copy)) { _, _ ->
copyToClipboard(crashInfo)
getString(R.string.copied).showToast()
exitApp()
}
.show()
.apply {
window?.decorView?.findViewById<TextView>(android.R.id.message)?.apply {
setTextIsSelectable(true)
textSize = 3.5f.sp
}
}
setFinishOnTouchOutside(false)
}
}
64 changes: 64 additions & 0 deletions app/src/main/java/com/skyd/anivu/util/CrashHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.skyd.anivu.util

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import com.skyd.anivu.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()

/**
* 当UncaughtException发生时会转入该函数来处理
*/
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)
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
<string name="rss_config_fragment_sync_wifi_constraint">仅在 Wi-Fi 连接时</string>
<string name="rss_config_fragment_sync_charging_constraint">仅在充电时</string>
<string name="rss_config_fragment_sync_battery_not_low_constraint">仅在电量充足时</string>
<string name="crashed">崩溃了</string>
<string name="submit_an_issue_on_github">报告</string>
<string name="copied">已复制</string>
<plurals name="rss_sync_frequency_minute">
<item quantity="other">每 %d 分钟</item>
</plurals>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
<string name="rss_config_fragment_sync_wifi_constraint">Only on Wi-Fi</string>
<string name="rss_config_fragment_sync_charging_constraint">Only when charging</string>
<string name="rss_config_fragment_sync_battery_not_low_constraint">Only not in low battery mode</string>
<string name="crashed">Crashed!</string>
<string name="submit_an_issue_on_github">Report</string>
<string name="copied">Copied</string>
<plurals name="rss_sync_frequency_minute">
<item quantity="one">Every %d minute</item>
<item quantity="other">Every %d minutes</item>
Expand Down

0 comments on commit 78e25fb

Please sign in to comment.