mihon/app/src/main/java/eu/kanade/tachiyomi/crash/GlobalExceptionHandler.kt
Andreas 4178f945c9
Add Crash activity (#8216)
* Add Crash activity

When the application crashes this sends them to a different activity with the cause message and an option to dump the crash logs

* Review changes
2022-10-16 16:35:20 -04:00

80 lines
2.9 KiB
Kotlin

package eu.kanade.tachiyomi.crash
import android.content.Context
import android.content.Intent
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import logcat.LogPriority
import kotlin.system.exitProcess
class GlobalExceptionHandler private constructor(
private val applicationContext: Context,
private val defaultHandler: Thread.UncaughtExceptionHandler,
private val activityToBeLaunched: Class<*>,
) : Thread.UncaughtExceptionHandler {
object ThrowableSerializer : KSerializer<Throwable> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Throwable", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): Throwable =
Throwable(message = decoder.decodeString())
override fun serialize(encoder: Encoder, value: Throwable) =
encoder.encodeString(value.stackTraceToString())
}
override fun uncaughtException(thread: Thread, exception: Throwable) {
try {
logcat(priority = LogPriority.ERROR, throwable = exception)
launchActivity(applicationContext, activityToBeLaunched, exception)
exitProcess(0)
} catch (_: Exception) {
defaultHandler.uncaughtException(thread, exception)
}
}
private fun launchActivity(
applicationContext: Context,
activity: Class<*>,
exception: Throwable,
) {
val intent = Intent(applicationContext, activity).apply {
putExtra(INTENT_EXTRA, Json.encodeToString(ThrowableSerializer, exception))
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
applicationContext.startActivity(intent)
}
companion object {
private const val INTENT_EXTRA = "Throwable"
fun initialize(
applicationContext: Context,
activityToBeLaunched: Class<*>,
) {
val handler = GlobalExceptionHandler(
applicationContext,
Thread.getDefaultUncaughtExceptionHandler() as Thread.UncaughtExceptionHandler,
activityToBeLaunched,
)
Thread.setDefaultUncaughtExceptionHandler(handler)
}
fun getThrowableFromIntent(intent: Intent): Throwable? {
return try {
Json.decodeFromString(ThrowableSerializer, intent.getStringExtra(INTENT_EXTRA)!!)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Wasn't able to retrive throwable from intent" }
null
}
}
}
}