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 { 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 } } } }